From 6edb3840d798101d026fe5b58ee9ce6ac8f975c5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 11 Apr 2015 15:52:36 +0200 Subject: [PATCH 01/34] Some early refactoring to support async miners better. --- exp/main.cpp | 2 +- libethcore/ProofOfWork.cpp | 30 ++++++--- libethcore/ProofOfWork.h | 22 +++---- libethereum/Client.cpp | 2 +- libethereum/Client.h | 4 +- libethereum/ClientBase.h | 2 +- libethereum/Interface.h | 2 +- libethereum/Miner.cpp | 6 +- libethereum/Miner.h | 77 +++++++++++++++++++++++ libethereum/State.cpp | 2 +- libethereum/State.h | 4 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 2 +- mix/MixClient.h | 2 +- test/blockchain.cpp | 4 +- 14 files changed, 123 insertions(+), 38 deletions(-) diff --git a/exp/main.cpp b/exp/main.cpp index 48562f80e..88f1075a9 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -114,7 +114,7 @@ int main() BlockInfo genesis = CanonBlockChain::genesis(); genesis.difficulty = 1 << 18; cdebug << (h256)u256((bigint(1) << 256) / genesis.difficulty); - std::pair r; + std::pair r; while (!r.first.completed) r = ecl.mine(genesis, 1000); cdebug << r.second.mixHash << r.second.nonce; diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index f7cf4944b..ffa787e3e 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -50,16 +50,16 @@ bool EthashPoW::verify(BlockInfo const& _header) return Ethasher::verify(_header); } -std::pair EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) +std::pair EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) { Ethasher::Miner m(_header); - std::pair ret; + std::pair ret; auto tid = std::this_thread::get_id(); static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng)); - h256 boundary = u256((bigint(1) << 256) / _header.difficulty); + h256 boundary = _header.boundary(); ret.first.requirement = log2((double)(u256)boundary); // 2^ 0 32 64 128 256 @@ -68,7 +68,7 @@ std::pair EthashCPU::mine(BlockInfo const& _header, // evaluate until we run out of time auto startTime = std::chrono::steady_clock::now(); double best = 1e99; // high enough to be effectively infinity :) - Proof result; + Solution result; unsigned hashCount = 0; for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++) { @@ -128,7 +128,7 @@ struct EthashCLHook: public ethash_cl_miner::search_hook { if (m_aborted) return; - cdebug << "Attempting to abort"; +// cdebug << "Attempting to abort"; m_abort = true; for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) std::this_thread::sleep_for(chrono::milliseconds(30)); @@ -148,14 +148,14 @@ protected: for (unsigned i = 0; i < _count; ++i) m_found.push_back((Nonce)(u64)_nonces[i]); m_aborted = true; - cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); +// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); return true; } virtual bool searched(uint64_t _startNonce, uint32_t _count) override { Guard l(x_all); - cdebug << "Searched" << _count << "from" << _startNonce; +// cdebug << "Searched" << _count << "from" << _startNonce; m_total += _count; m_last = _startNonce + _count; if (m_abort) @@ -184,7 +184,7 @@ EthashCL::~EthashCL() { } -std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) +std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) { if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash()) { @@ -206,7 +206,14 @@ std::pair EthashCL::mine(BlockInfo const& _header, unsi } m_lastHeader = _header; + MineInfo mi; + Solution proof; + mi.requirement = log2((double)(u256)_header.boundary()); + mi.best = 0; + std::this_thread::sleep_for(chrono::milliseconds(_msTimeout)); + + mi.hashes += m_hook->fetchTotal(); auto found = m_hook->fetchFound(); if (!found.empty()) { @@ -214,10 +221,13 @@ std::pair EthashCL::mine(BlockInfo const& _header, unsi { auto result = Ethasher::eval(_header, n); if (result.value < _header.boundary()) - return std::make_pair(MineInfo(true), EthashCL::Proof{n, result.mixHash}); + { + mi.completed = true; + proof = Solution{n, result.mixHash}; + } } } - return std::make_pair(MineInfo(false), EthashCL::Proof()); + return std::make_pair(mi, proof); } #endif diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 2e04e842c..bd8ab58db 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -54,23 +54,23 @@ struct MineInfo class EthashPoW { public: - struct Proof + struct Solution { Nonce nonce; h256 mixHash; }; static bool verify(BlockInfo const& _header); - static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } virtual unsigned defaultTimeout() const { return 100; } - virtual std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0; + virtual std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0; }; class EthashCPU: public EthashPoW { public: - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; + std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; protected: Nonce m_last; @@ -85,7 +85,7 @@ public: EthashCL(); ~EthashCL(); - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; + std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; unsigned defaultTimeout() const override { return 500; } protected: @@ -105,11 +105,11 @@ template class ProofOfWorkEngine: public Evaluator { public: - using Proof = Nonce; + using Solution = Nonce; static bool verify(BlockInfo const& _header) { return (bigint)(u256)Evaluator::eval(_header.headerHash(WithoutNonce), _header.nonce) <= (bigint(1) << 256) / _header.difficulty; } - inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true); - static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r; } + inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true); + static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r; } unsigned defaultTimeout() const { return 100; } protected: @@ -127,7 +127,7 @@ using SHA3ProofOfWork = ProofOfWorkEngine; using ProofOfWork = Ethash; template -std::pair::Proof> ProofOfWorkEngine::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) +std::pair::Solution> ProofOfWorkEngine::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) { auto headerHashWithoutNonce = _header.headerHash(WithoutNonce); auto difficulty = _header.difficulty; @@ -145,11 +145,11 @@ std::pair::Proof> ProofOfWorkEng // evaluate until we run out of time auto startTime = std::chrono::steady_clock::now(); double best = 1e99; // high enough to be effectively infinity :) - ProofOfWorkEngine::Proof solution; + ProofOfWorkEngine::Solution solution; unsigned h = 0; for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++) { - solution = (ProofOfWorkEngine::Proof)s; + solution = (ProofOfWorkEngine::Solution)s; auto e = (bigint)(u256)Evaluator::eval(headerHashWithoutNonce, solution); best = std::min(best, log2((double)e)); if (e <= d) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 4b197797f..223384c3a 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -452,7 +452,7 @@ pair Client::getWork() return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); } -bool Client::submitWork(ProofOfWork::Proof const& _proof) +bool Client::submitWork(ProofOfWork::Solution const& _proof) { Guard l(x_remoteMiner); return m_remoteMiner.submitWork(_proof); diff --git a/libethereum/Client.h b/libethereum/Client.h index cc51f9747..ec852afd2 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -82,7 +82,7 @@ public: h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); } u256 const& difficulty() const { return m_state.info().difficulty; } - bool submitWork(ProofOfWork::Proof const& _result) { return (m_isComplete = m_state.completeMine(_result)); } + bool submitWork(ProofOfWork::Solution const& _result) { return (m_isComplete = m_state.completeMine(_result)); } virtual bool isComplete() const override { return m_isComplete; } virtual bytes const& blockData() const { return m_state.blockData(); } @@ -216,7 +216,7 @@ public: /// nonce (the 'work hash') and the difficulty to be met. virtual std::pair getWork() override; /// Submit the proof for the proof-of-work. - virtual bool submitWork(ProofOfWork::Proof const& _proof) override; + virtual bool submitWork(ProofOfWork::Solution const& _proof) override; // Debug stuff: diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ddfbf1176..ae6d27578 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -150,7 +150,7 @@ public: virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::hashrate")); } virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); } virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } - virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } + virtual bool submitWork(eth::ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } State asOf(BlockNumber _h) const; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 02833743e..cf2e7f5ea 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -190,7 +190,7 @@ public: /// Get hash of the current block to be mined minus the nonce (the 'work hash'). virtual std::pair getWork() = 0; /// Submit the nonce for the proof-of-work. - virtual bool submitWork(ProofOfWork::Proof const& _proof) = 0; + virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0; /// Check the progress of the mining. virtual MineProgress miningProgress() const = 0; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index b3a65f081..dc3d9bd9e 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -30,11 +30,9 @@ using namespace dev::eth; Miner::~Miner() {} -LocalMiner::LocalMiner(MinerHost* _host, unsigned _id): - AsyncMiner(_host, _id), - Worker("miner-" + toString(_id)) +LocalMiner::LocalMiner(MinerHost* _host, unsigned _id) { - m_pow.reset(_host->turbo() ? new Ethash : (Ethash*)new EthashCPU); + setup(_host, _id); } void LocalMiner::setup(MinerHost* _host, unsigned _id) diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 8c2fc4bb4..86d103db5 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -36,6 +36,15 @@ namespace dev namespace eth { +struct WorkPackage +{ + h256 boundary; + h256 headerHash; ///< When h256() means "pause until notified a new work package is available". + h256 seedHash; +}; + +static const WorkPackage NullWorkPackage; + /** * @brief Describes the progress of a mining operation. */ @@ -57,6 +66,10 @@ struct MineProgress class MinerHost { public: + // ============================= NEW API ============================= + virtual WorkPackage const& getWork() const { return NullWorkPackage; } + + // ============================= OLD API ============================= virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. virtual void onProgressed() {} ///< Called once some progress has been made. virtual void onComplete() {} ///< Called once a block is found. @@ -174,5 +187,69 @@ private: std::list m_mineHistory; ///< What the history of our mining? }; +/** + * @brief A collective of Miners. + * Miners ask for work, then submit proofs + * @threadsafe + */ +class Farm: public MinerHost +{ +public: + /** + * @brief Sets the current mining mission. + * @param _bi The block (header) we wish to be mining. + */ + void setWork(BlockInfo const& _bi); + + /** + * @brief (Re)start miners for CPU only. + * @returns true if started properly. + */ + bool startCPU(); + + /** + * @brief (Re)start miners for GPU only. + * @returns true if started properly. + */ + bool startGPU(); + + /** + * @brief Stop all mining activities. + */ + void stop(); + + /** + * @brief Get information on the progress of mining this work package. + * @return The progress with mining so far. + */ + MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } + +protected: + /** + * @brief Called by a Miner to retrieve a work package. Reimplemented from MinerHost. + * @return The work package to solve. + */ + virtual WorkPackage const& getWork() const override { ReadGuard l(x_work); return m_work; } + + /** + * @brief Called from a Miner to note a WorkPackage has a solution. + * @param _p The solution. + * @param _wp The WorkPackage that the Solution is for. + * @return true iff the solution was good (implying that mining should be . + */ + virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; + +private: + mutable SharedMutex x_miners; + std::vector> m_miners; + + mutable SharedMutex x_progress; + MineProgress m_progress; + + mutable SharedMutex x_work; + WorkPackage m_work; +}; + + } } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c1beee787..6e94a3406 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -856,7 +856,7 @@ void State::commitToMine(BlockChain const& _bc) m_committedToMine = true; } -bool State::completeMine(ProofOfWork::Proof const& _nonce) +bool State::completeMine(ProofOfWork::Solution const& _nonce) { ProofOfWork::assignResult(_nonce, m_currentBlock); diff --git a/libethereum/State.h b/libethereum/State.h index 1b71038b4..b327378a1 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -162,7 +162,7 @@ public: /// Pass in a solution to the proof-of-work. /// @returns true iff the given nonce is a proof-of-work for this State's block. - bool completeMine(ProofOfWork::Proof const& _result); + bool completeMine(ProofOfWork::Solution const& _result); /// Attempt to find valid nonce for block that this state represents. /// This function is thread-safe. You can safely have other interactions with this object while it is happening. @@ -174,7 +174,7 @@ public: m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); MineInfo ret; - typename ProofOfWork::Proof r; + typename ProofOfWork::Solution r; std::tie(ret, r) = _pow->mine(m_currentBlock, _pow->defaultTimeout(), true); if (!ret.completed) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 04bbe7345..e987e64cc 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -767,7 +767,7 @@ bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const& { try { - return client()->submitWork(ProofOfWork::Proof{jsToFixed(_nonce), jsToFixed<32>(_mixHash)}); + return client()->submitWork(ProofOfWork::Solution{jsToFixed(_nonce), jsToFixed<32>(_mixHash)}); } catch (...) { diff --git a/mix/MixClient.h b/mix/MixClient.h index a9ab30048..a5ecbf465 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -71,7 +71,7 @@ public: uint64_t hashrate() const override; eth::MineProgress miningProgress() const override; std::pair getWork() override { return std::pair(); } - bool submitWork(eth::ProofOfWork::Proof const&) override { return false; } + bool submitWork(eth::ProofOfWork::Solution const&) override { return false; } virtual void flushTransactions() override {} /// @returns the last mined block information diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 15cda8037..6d7bc97ef 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -575,7 +575,7 @@ void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj) _currentBlockHeader = tmp; ProofOfWork pow; - std::pair ret; + std::pair ret; while (!ProofOfWork::verify(_currentBlockHeader)) { ret = pow.mine(_currentBlockHeader, 1000, true); @@ -621,7 +621,7 @@ BlockInfo constructBlock(mObject& _o) void updatePoW(BlockInfo& _bi) { ProofOfWork pow; - std::pair ret; + std::pair ret; while (!ProofOfWork::verify(_bi)) { ret = pow.mine(_bi, 10000, true); From fae9208c0663dd677ec6ba5d055286cd63252fe6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 11 Apr 2015 15:55:01 +0200 Subject: [PATCH 02/34] Add more files. --- libethash-cl/CMakeLists.txt | 47 + libethash-cl/bin2h.cmake | 86 + libethash-cl/cl.hpp | 4014 ++++++++++++++++++++++++ libethash-cl/ethash_cl_miner.cpp | 332 ++ libethash-cl/ethash_cl_miner.h | 43 + libethash-cl/ethash_cl_miner_kernel.cl | 460 +++ 6 files changed, 4982 insertions(+) create mode 100644 libethash-cl/CMakeLists.txt create mode 100644 libethash-cl/bin2h.cmake create mode 100644 libethash-cl/cl.hpp create mode 100644 libethash-cl/ethash_cl_miner.cpp create mode 100644 libethash-cl/ethash_cl_miner.h create mode 100644 libethash-cl/ethash_cl_miner_kernel.cl diff --git a/libethash-cl/CMakeLists.txt b/libethash-cl/CMakeLists.txt new file mode 100644 index 000000000..7b00a22bd --- /dev/null +++ b/libethash-cl/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.8) + +set(LIBRARY ethash-cl) +#set(CMAKE_BUILD_TYPE Release) + +include(bin2h.cmake) +bin2h(SOURCE_FILE ethash_cl_miner_kernel.cl + VARIABLE_NAME ethash_cl_miner_kernel + HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) + +if (NOT MSVC) + # Initialize CXXFLAGS for c++11 + set(CMAKE_CXX_FLAGS "-Wall -std=c++11") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") + + # Compiler-specific C++11 activation. + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)) + message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.") + endif () + elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + else () + message(FATAL_ERROR "Your C++ compiler does not support C++11.") + endif () +endif() + +set(OpenCL_FOUND TRUE) +set(OpenCL_INCLUDE_DIRS /usr/include/CL) +set(OpenCL_LIBRARIES -lOpenCL) + +if (NOT OpenCL_FOUND) + find_package(OpenCL) +endif() + +if (OpenCL_FOUND) + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -Werror -pedantic -fPIC ${CMAKE_CXX_FLAGS}") + include_directories(${OpenCL_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) + include_directories(..) + add_library(${LIBRARY} ethash_cl_miner.cpp ethash_cl_miner.h cl.hpp) + TARGET_LINK_LIBRARIES(${LIBRARY} ${OpenCL_LIBRARIES} ethash) +endif() diff --git a/libethash-cl/bin2h.cmake b/libethash-cl/bin2h.cmake new file mode 100644 index 000000000..90ca9cc5b --- /dev/null +++ b/libethash-cl/bin2h.cmake @@ -0,0 +1,86 @@ +# https://gist.github.com/sivachandran/3a0de157dccef822a230 +include(CMakeParseArguments) + +# Function to wrap a given string into multiple lines at the given column position. +# Parameters: +# VARIABLE - The name of the CMake variable holding the string. +# AT_COLUMN - The column position at which string will be wrapped. +function(WRAP_STRING) + set(oneValueArgs VARIABLE AT_COLUMN) + cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN}) + + string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength) + math(EXPR offset "0") + + while(stringLength GREATER 0) + + if(stringLength GREATER ${WRAP_STRING_AT_COLUMN}) + math(EXPR length "${WRAP_STRING_AT_COLUMN}") + else() + math(EXPR length "${stringLength}") + endif() + + string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line) + set(lines "${lines}\n${line}") + + math(EXPR stringLength "${stringLength} - ${length}") + math(EXPR offset "${offset} + ${length}") + endwhile() + + set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE) +endfunction() + +# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file +# will contain a byte array and integer variable holding the size of the array. +# Parameters +# SOURCE_FILE - The path of source file whose contents will be embedded in the header file. +# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append +# to this name and will be used a variable name for size variable. +# HEADER_FILE - The path of header file. +# APPEND - If specified appends to the header file instead of overwriting it +# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be +# useful if the source file is a text file and we want to use the file contents +# as string. But the size variable holds size of the byte array without this +# null byte. +# Usage: +# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG") +function(BIN2H) + set(options APPEND NULL_TERMINATE) + set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE) + cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN}) + + # reads source file contents as hex string + file(READ ${BIN2H_SOURCE_FILE} hexString HEX) + string(LENGTH ${hexString} hexStringLength) + + # appends null byte if asked + if(BIN2H_NULL_TERMINATE) + set(hexString "${hexString}00") + endif() + + # wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line) + wrap_string(VARIABLE hexString AT_COLUMN 32) + math(EXPR arraySize "${hexStringLength} / 2") + + # adds '0x' prefix and comma suffix before and after every byte respectively + string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString}) + # removes trailing comma + string(REGEX REPLACE ", $" "" arrayValues ${arrayValues}) + + # converts the variable name into proper C identifier + IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake + string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) + ENDIF() + string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) + + # declares byte array and the length variables + set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };") + set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};") + + set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n") + if(BIN2H_APPEND) + file(APPEND ${BIN2H_HEADER_FILE} "${declarations}") + else() + file(WRITE ${BIN2H_HEADER_FILE} "${declarations}") + endif() +endfunction() diff --git a/libethash-cl/cl.hpp b/libethash-cl/cl.hpp new file mode 100644 index 000000000..a38498762 --- /dev/null +++ b/libethash-cl/cl.hpp @@ -0,0 +1,4014 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/*! \file + * + * \brief C++ bindings for OpenCL 1.0 (rev 48) and OpenCL 1.1 (rev 33) + * \author Benedict R. Gaster and Laurent Morichetti + * + * Additions and fixes from Brian Cole, March 3rd 2010. + * + * \version 1.1 + * \date June 2010 + * + * Optional extension support + * + * cl + * cl_ext_device_fission + * #define USE_CL_DEVICE_FISSION + */ + +/*! \mainpage + * \section intro Introduction + * For many large applications C++ is the language of choice and so it seems + * reasonable to define C++ bindings for OpenCL. + * + * + * The interface is contained with a single C++ header file \em cl.hpp and all + * definitions are contained within the namespace \em cl. There is no additional + * requirement to include \em cl.h and to use either the C++ or original C + * bindings it is enough to simply include \em cl.hpp. + * + * The bindings themselves are lightweight and correspond closely to the + * underlying C API. Using the C++ bindings introduces no additional execution + * overhead. + * + * For detail documentation on the bindings see: + * + * The OpenCL C++ Wrapper API 1.1 (revision 04) + * http://www.khronos.org/registry/cl/specs/opencl-cplusplus-1.1.pdf + * + * \section example Example + * + * The following example shows a general use case for the C++ + * bindings, including support for the optional exception feature and + * also the supplied vector and string classes, see following sections for + * decriptions of these features. + * + * \code + * #define __CL_ENABLE_EXCEPTIONS + * + * #if defined(__APPLE__) || defined(__MACOSX) + * #include + * #else + * #include + * #endif + * #include + * #include + * #include + * + * const char * helloStr = "__kernel void " + * "hello(void) " + * "{ " + * " " + * "} "; + * + * int + * main(void) + * { + * cl_int err = CL_SUCCESS; + * try { + * + * std::vector platforms; + * cl::Platform::get(&platforms); + * if (platforms.size() == 0) { + * std::cout << "Platform size 0\n"; + * return -1; + * } + * + * cl_context_properties properties[] = + * { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; + * cl::Context context(CL_DEVICE_TYPE_CPU, properties); + * + * std::vector devices = context.getInfo(); + * + * cl::Program::Sources source(1, + * std::make_pair(helloStr,strlen(helloStr))); + * cl::Program program_ = cl::Program(context, source); + * program_.build(devices); + * + * cl::Kernel kernel(program_, "hello", &err); + * + * cl::Event event; + * cl::CommandQueue queue(context, devices[0], 0, &err); + * queue.enqueueNDRangeKernel( + * kernel, + * cl::NullRange, + * cl::NDRange(4,4), + * cl::NullRange, + * NULL, + * &event); + * + * event.wait(); + * } + * catch (cl::Error err) { + * std::cerr + * << "ERROR: " + * << err.what() + * << "(" + * << err.err() + * << ")" + * << std::endl; + * } + * + * return EXIT_SUCCESS; + * } + * + * \endcode + * + */ +#ifndef CL_HPP_ +#define CL_HPP_ + +#ifdef _WIN32 +#include +#include +#if defined(USE_DX_INTEROP) +#include +#endif +#endif // _WIN32 + +// +#if defined(USE_CL_DEVICE_FISSION) +#include +#endif + +#if defined(__APPLE__) || defined(__MACOSX) +#include +#include +#else +#include +#include +#endif // !__APPLE__ + +#if !defined(CL_CALLBACK) +#define CL_CALLBACK +#endif //CL_CALLBACK + +#include + +#if !defined(__NO_STD_VECTOR) +#include +#endif + +#if !defined(__NO_STD_STRING) +#include +#endif + +#if defined(linux) || defined(__APPLE__) || defined(__MACOSX) +# include +#endif // linux + +#include + +/*! \namespace cl + * + * \brief The OpenCL C++ bindings are defined within this namespace. + * + */ +namespace cl { + +#define __INIT_CL_EXT_FCN_PTR(name) \ + if(!pfn_##name) { \ + pfn_##name = (PFN_##name) \ + clGetExtensionFunctionAddress(#name); \ + if(!pfn_##name) { \ + } \ + } + +class Program; +class Device; +class Context; +class CommandQueue; +class Memory; + +#if defined(__CL_ENABLE_EXCEPTIONS) +#include +/*! \class Error + * \brief Exception class + */ +class Error : public std::exception +{ +private: + cl_int err_; + const char * errStr_; +public: + /*! Create a new CL error exception for a given error code + * and corresponding message. + */ + Error(cl_int err, const char * errStr = NULL) : err_(err), errStr_(errStr) + {} + + ~Error() throw() {} + + /*! \brief Get error string associated with exception + * + * \return A memory pointer to the error message string. + */ + virtual const char * what() const throw () + { + if (errStr_ == NULL) { + return "empty"; + } + else { + return errStr_; + } + } + + /*! \brief Get error code associated with exception + * + * \return The error code. + */ + cl_int err(void) const { return err_; } +}; + +#define __ERR_STR(x) #x +#else +#define __ERR_STR(x) NULL +#endif // __CL_ENABLE_EXCEPTIONS + +//! \cond DOXYGEN_DETAIL +#if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) +#define __GET_DEVICE_INFO_ERR __ERR_STR(clgetDeviceInfo) +#define __GET_PLATFORM_INFO_ERR __ERR_STR(clGetPlatformInfo) +#define __GET_DEVICE_IDS_ERR __ERR_STR(clGetDeviceIDs) +#define __GET_PLATFORM_IDS_ERR __ERR_STR(clGetPlatformIDs) +#define __GET_CONTEXT_INFO_ERR __ERR_STR(clGetContextInfo) +#define __GET_EVENT_INFO_ERR __ERR_STR(clGetEventInfo) +#define __GET_EVENT_PROFILE_INFO_ERR __ERR_STR(clGetEventProfileInfo) +#define __GET_MEM_OBJECT_INFO_ERR __ERR_STR(clGetMemObjectInfo) +#define __GET_IMAGE_INFO_ERR __ERR_STR(clGetImageInfo) +#define __GET_SAMPLER_INFO_ERR __ERR_STR(clGetSamplerInfo) +#define __GET_KERNEL_INFO_ERR __ERR_STR(clGetKernelInfo) +#define __GET_KERNEL_WORK_GROUP_INFO_ERR __ERR_STR(clGetKernelWorkGroupInfo) +#define __GET_PROGRAM_INFO_ERR __ERR_STR(clGetProgramInfo) +#define __GET_PROGRAM_BUILD_INFO_ERR __ERR_STR(clGetProgramBuildInfo) +#define __GET_COMMAND_QUEUE_INFO_ERR __ERR_STR(clGetCommandQueueInfo) + +#define __CREATE_CONTEXT_FROM_TYPE_ERR __ERR_STR(clCreateContextFromType) +#define __GET_SUPPORTED_IMAGE_FORMATS_ERR __ERR_STR(clGetSupportedImageFormats) + +#define __CREATE_BUFFER_ERR __ERR_STR(clCreateBuffer) +#define __CREATE_SUBBUFFER_ERR __ERR_STR(clCreateSubBuffer) +#define __CREATE_GL_BUFFER_ERR __ERR_STR(clCreateFromGLBuffer) +#define __GET_GL_OBJECT_INFO_ERR __ERR_STR(clGetGLObjectInfo) +#define __CREATE_IMAGE2D_ERR __ERR_STR(clCreateImage2D) +#define __CREATE_IMAGE3D_ERR __ERR_STR(clCreateImage3D) +#define __CREATE_SAMPLER_ERR __ERR_STR(clCreateSampler) +#define __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR __ERR_STR(clSetMemObjectDestructorCallback) + +#define __CREATE_USER_EVENT_ERR __ERR_STR(clCreateUserEvent) +#define __SET_USER_EVENT_STATUS_ERR __ERR_STR(clSetUserEventStatus) +#define __SET_EVENT_CALLBACK_ERR __ERR_STR(clSetEventCallback) +#define __WAIT_FOR_EVENTS_ERR __ERR_STR(clWaitForEvents) + +#define __CREATE_KERNEL_ERR __ERR_STR(clCreateKernel) +#define __SET_KERNEL_ARGS_ERR __ERR_STR(clSetKernelArg) +#define __CREATE_PROGRAM_WITH_SOURCE_ERR __ERR_STR(clCreateProgramWithSource) +#define __CREATE_PROGRAM_WITH_BINARY_ERR __ERR_STR(clCreateProgramWithBinary) +#define __BUILD_PROGRAM_ERR __ERR_STR(clBuildProgram) +#define __CREATE_KERNELS_IN_PROGRAM_ERR __ERR_STR(clCreateKernelsInProgram) + +#define __CREATE_COMMAND_QUEUE_ERR __ERR_STR(clCreateCommandQueue) +#define __SET_COMMAND_QUEUE_PROPERTY_ERR __ERR_STR(clSetCommandQueueProperty) +#define __ENQUEUE_READ_BUFFER_ERR __ERR_STR(clEnqueueReadBuffer) +#define __ENQUEUE_READ_BUFFER_RECT_ERR __ERR_STR(clEnqueueReadBufferRect) +#define __ENQUEUE_WRITE_BUFFER_ERR __ERR_STR(clEnqueueWriteBuffer) +#define __ENQUEUE_WRITE_BUFFER_RECT_ERR __ERR_STR(clEnqueueWriteBufferRect) +#define __ENQEUE_COPY_BUFFER_ERR __ERR_STR(clEnqueueCopyBuffer) +#define __ENQEUE_COPY_BUFFER_RECT_ERR __ERR_STR(clEnqueueCopyBufferRect) +#define __ENQUEUE_READ_IMAGE_ERR __ERR_STR(clEnqueueReadImage) +#define __ENQUEUE_WRITE_IMAGE_ERR __ERR_STR(clEnqueueWriteImage) +#define __ENQUEUE_COPY_IMAGE_ERR __ERR_STR(clEnqueueCopyImage) +#define __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR __ERR_STR(clEnqueueCopyImageToBuffer) +#define __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR __ERR_STR(clEnqueueCopyBufferToImage) +#define __ENQUEUE_MAP_BUFFER_ERR __ERR_STR(clEnqueueMapBuffer) +#define __ENQUEUE_MAP_IMAGE_ERR __ERR_STR(clEnqueueMapImage) +#define __ENQUEUE_UNMAP_MEM_OBJECT_ERR __ERR_STR(clEnqueueUnMapMemObject) +#define __ENQUEUE_NDRANGE_KERNEL_ERR __ERR_STR(clEnqueueNDRangeKernel) +#define __ENQUEUE_TASK_ERR __ERR_STR(clEnqueueTask) +#define __ENQUEUE_NATIVE_KERNEL __ERR_STR(clEnqueueNativeKernel) +#define __ENQUEUE_MARKER_ERR __ERR_STR(clEnqueueMarker) +#define __ENQUEUE_WAIT_FOR_EVENTS_ERR __ERR_STR(clEnqueueWaitForEvents) +#define __ENQUEUE_BARRIER_ERR __ERR_STR(clEnqueueBarrier) + +#define __ENQUEUE_ACQUIRE_GL_ERR __ERR_STR(clEnqueueAcquireGLObjects) +#define __ENQUEUE_RELEASE_GL_ERR __ERR_STR(clEnqueueReleaseGLObjects) + +#define __UNLOAD_COMPILER_ERR __ERR_STR(clUnloadCompiler) + +#define __FLUSH_ERR __ERR_STR(clFlush) +#define __FINISH_ERR __ERR_STR(clFinish) + +#define __CREATE_SUB_DEVICES __ERR_STR(clCreateSubDevicesEXT) +#endif // __CL_USER_OVERRIDE_ERROR_STRINGS +//! \endcond + +/*! \class string + * \brief Simple string class, that provides a limited subset of std::string + * functionality but avoids many of the issues that come with that class. + */ +class string +{ +private: + ::size_t size_; + char * str_; +public: + string(void) : size_(0), str_(NULL) + { + } + + string(char * str, ::size_t size) : + size_(size), + str_(NULL) + { + str_ = new char[size_+1]; + if (str_ != NULL) { + memcpy(str_, str, size_ * sizeof(char)); + str_[size_] = '\0'; + } + else { + size_ = 0; + } + } + + string(char * str) : + str_(NULL) + { + size_= ::strlen(str); + str_ = new char[size_ + 1]; + if (str_ != NULL) { + memcpy(str_, str, (size_ + 1) * sizeof(char)); + } + else { + size_ = 0; + } + } + + string& operator=(const string& rhs) + { + if (this == &rhs) { + return *this; + } + + if (rhs.size_ == 0 || rhs.str_ == NULL) { + size_ = 0; + str_ = NULL; + } + else { + size_ = rhs.size_; + str_ = new char[size_ + 1]; + if (str_ != NULL) { + memcpy(str_, rhs.str_, (size_ + 1) * sizeof(char)); + } + else { + size_ = 0; + } + } + + return *this; + } + + string(const string& rhs) + { + *this = rhs; + } + + ~string() + { + if (str_ != NULL) { + delete[] str_; + } + } + + ::size_t size(void) const { return size_; } + ::size_t length(void) const { return size(); } + + const char * c_str(void) const { return (str_) ? str_ : "";} +}; + +#if !defined(__USE_DEV_STRING) && !defined(__NO_STD_STRING) +#include +typedef std::string STRING_CLASS; +#elif !defined(__USE_DEV_STRING) +typedef cl::string STRING_CLASS; +#endif + +#if !defined(__USE_DEV_VECTOR) && !defined(__NO_STD_VECTOR) +#include +#define VECTOR_CLASS std::vector +#elif !defined(__USE_DEV_VECTOR) +#define VECTOR_CLASS cl::vector +#endif + +#if !defined(__MAX_DEFAULT_VECTOR_SIZE) +#define __MAX_DEFAULT_VECTOR_SIZE 10 +#endif + +/*! \class vector + * \brief Fixed sized vector implementation that mirroring + * std::vector functionality. + */ +template +class vector +{ +private: + T data_[N]; + unsigned int size_; + bool empty_; +public: + vector() : + size_(-1), + empty_(true) + {} + + ~vector() {} + + unsigned int size(void) const + { + return size_ + 1; + } + + void clear() + { + size_ = -1; + empty_ = true; + } + + void push_back (const T& x) + { + if (size() < N) { + size_++; + data_[size_] = x; + empty_ = false; + } + } + + void pop_back(void) + { + if (!empty_) { + data_[size_].~T(); + size_--; + if (size_ == -1) { + empty_ = true; + } + } + } + + vector(const vector& vec) : + size_(vec.size_), + empty_(vec.empty_) + { + if (!empty_) { + memcpy(&data_[0], &vec.data_[0], size() * sizeof(T)); + } + } + + vector(unsigned int size, const T& val = T()) : + size_(-1), + empty_(true) + { + for (unsigned int i = 0; i < size; i++) { + push_back(val); + } + } + + vector& operator=(const vector& rhs) + { + if (this == &rhs) { + return *this; + } + + size_ = rhs.size_; + empty_ = rhs.empty_; + + if (!empty_) { + memcpy(&data_[0], &rhs.data_[0], size() * sizeof(T)); + } + + return *this; + } + + bool operator==(vector &vec) + { + if (empty_ && vec.empty_) { + return true; + } + + if (size() != vec.size()) { + return false; + } + + return memcmp(&data_[0], &vec.data_[0], size() * sizeof(T)) == 0 ? true : false; + } + + operator T* () { return data_; } + operator const T* () const { return data_; } + + bool empty (void) const + { + return empty_; + } + + unsigned int max_size (void) const + { + return N; + } + + unsigned int capacity () const + { + return sizeof(T) * N; + } + + T& operator[](int index) + { + return data_[index]; + } + + T operator[](int index) const + { + return data_[index]; + } + + template + void assign(I start, I end) + { + clear(); + while(start < end) { + push_back(*start); + start++; + } + } + + /*! \class iterator + * \brief Iterator class for vectors + */ + class iterator + { + private: + vector vec_; + int index_; + bool initialized_; + public: + iterator(void) : + index_(-1), + initialized_(false) + { + index_ = -1; + initialized_ = false; + } + + ~iterator(void) {} + + static iterator begin(vector &vec) + { + iterator i; + + if (!vec.empty()) { + i.index_ = 0; + } + + i.vec_ = vec; + i.initialized_ = true; + return i; + } + + static iterator end(vector &vec) + { + iterator i; + + if (!vec.empty()) { + i.index_ = vec.size(); + } + i.vec_ = vec; + i.initialized_ = true; + return i; + } + + bool operator==(iterator i) + { + return ((vec_ == i.vec_) && + (index_ == i.index_) && + (initialized_ == i.initialized_)); + } + + bool operator!=(iterator i) + { + return (!(*this==i)); + } + + void operator++() + { + index_++; + } + + void operator++(int x) + { + index_ += x; + } + + void operator--() + { + index_--; + } + + void operator--(int x) + { + index_ -= x; + } + + T operator *() + { + return vec_[index_]; + } + }; + + iterator begin(void) + { + return iterator::begin(*this); + } + + iterator end(void) + { + return iterator::end(*this); + } + + T& front(void) + { + return data_[0]; + } + + T& back(void) + { + return data_[size_]; + } + + const T& front(void) const + { + return data_[0]; + } + + const T& back(void) const + { + return data_[size_]; + } +}; + +/*! + * \brief size_t class used to interface between C++ and + * OpenCL C calls that require arrays of size_t values, who's + * size is known statically. + */ +template +struct size_t : public cl::vector< ::size_t, N> { }; + +namespace detail { + +// GetInfo help struct +template +struct GetInfoHelper +{ + static cl_int + get(Functor f, cl_uint name, T* param) + { + return f(name, sizeof(T), param, NULL); + } +}; + +// Specialized GetInfoHelper for VECTOR_CLASS params +template +struct GetInfoHelper > +{ + static cl_int get(Func f, cl_uint name, VECTOR_CLASS* param) + { + ::size_t required; + cl_int err = f(name, 0, NULL, &required); + if (err != CL_SUCCESS) { + return err; + } + + T* value = (T*) alloca(required); + err = f(name, required, value, NULL); + if (err != CL_SUCCESS) { + return err; + } + + param->assign(&value[0], &value[required/sizeof(T)]); + return CL_SUCCESS; + } +}; + +// Specialized for getInfo +template +struct GetInfoHelper > +{ + static cl_int + get(Func f, cl_uint name, VECTOR_CLASS* param) + { + cl_uint err = f(name, param->size() * sizeof(char *), &(*param)[0], NULL); + if (err != CL_SUCCESS) { + return err; + } + + return CL_SUCCESS; + } +}; + +// Specialized GetInfoHelper for STRING_CLASS params +template +struct GetInfoHelper +{ + static cl_int get(Func f, cl_uint name, STRING_CLASS* param) + { + ::size_t required; + cl_int err = f(name, 0, NULL, &required); + if (err != CL_SUCCESS) { + return err; + } + + char* value = (char*) alloca(required); + err = f(name, required, value, NULL); + if (err != CL_SUCCESS) { + return err; + } + + *param = value; + return CL_SUCCESS; + } +}; + +#define __GET_INFO_HELPER_WITH_RETAIN(CPP_TYPE) \ +namespace detail { \ +template \ +struct GetInfoHelper \ +{ \ + static cl_int get(Func f, cl_uint name, CPP_TYPE* param) \ + { \ + cl_uint err = f(name, sizeof(CPP_TYPE), param, NULL); \ + if (err != CL_SUCCESS) { \ + return err; \ + } \ + \ + return ReferenceHandler::retain((*param)()); \ + } \ +}; \ +} + + +#define __PARAM_NAME_INFO_1_0(F) \ + F(cl_platform_info, CL_PLATFORM_PROFILE, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_VERSION, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_NAME, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_VENDOR, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_EXTENSIONS, STRING_CLASS) \ + \ + F(cl_device_info, CL_DEVICE_TYPE, cl_device_type) \ + F(cl_device_info, CL_DEVICE_VENDOR_ID, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_GROUP_SIZE, ::size_t) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_SIZES, VECTOR_CLASS< ::size_t>) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint) \ + F(cl_device_info, CL_DEVICE_ADDRESS_BITS, cl_bitfield) \ + F(cl_device_info, CL_DEVICE_MAX_READ_IMAGE_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_WIDTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_HEIGHT, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_WIDTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_HEIGHT, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_DEPTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE_SUPPORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_PARAMETER_SIZE, ::size_t) \ + F(cl_device_info, CL_DEVICE_MAX_SAMPLERS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint) \ + F(cl_device_info, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, cl_uint) \ + F(cl_device_info, CL_DEVICE_SINGLE_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, cl_device_mem_cache_type) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint)\ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_MAX_CONSTANT_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_LOCAL_MEM_TYPE, cl_device_local_mem_type) \ + F(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool) \ + F(cl_device_info, CL_DEVICE_PROFILING_TIMER_RESOLUTION, ::size_t) \ + F(cl_device_info, CL_DEVICE_ENDIAN_LITTLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_AVAILABLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_COMPILER_AVAILABLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_EXECUTION_CAPABILITIES, cl_device_exec_capabilities) \ + F(cl_device_info, CL_DEVICE_QUEUE_PROPERTIES, cl_command_queue_properties) \ + F(cl_device_info, CL_DEVICE_PLATFORM, cl_platform_id) \ + F(cl_device_info, CL_DEVICE_NAME, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_VENDOR, STRING_CLASS) \ + F(cl_device_info, CL_DRIVER_VERSION, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_PROFILE, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_VERSION, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_EXTENSIONS, STRING_CLASS) \ + \ + F(cl_context_info, CL_CONTEXT_REFERENCE_COUNT, cl_uint) \ + F(cl_context_info, CL_CONTEXT_DEVICES, VECTOR_CLASS) \ + F(cl_context_info, CL_CONTEXT_PROPERTIES, VECTOR_CLASS) \ + \ + F(cl_event_info, CL_EVENT_COMMAND_QUEUE, cl::CommandQueue) \ + F(cl_event_info, CL_EVENT_COMMAND_TYPE, cl_command_type) \ + F(cl_event_info, CL_EVENT_REFERENCE_COUNT, cl_uint) \ + F(cl_event_info, CL_EVENT_COMMAND_EXECUTION_STATUS, cl_uint) \ + \ + F(cl_profiling_info, CL_PROFILING_COMMAND_QUEUED, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_SUBMIT, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_START, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_END, cl_ulong) \ + \ + F(cl_mem_info, CL_MEM_TYPE, cl_mem_object_type) \ + F(cl_mem_info, CL_MEM_FLAGS, cl_mem_flags) \ + F(cl_mem_info, CL_MEM_SIZE, ::size_t) \ + F(cl_mem_info, CL_MEM_HOST_PTR, void*) \ + F(cl_mem_info, CL_MEM_MAP_COUNT, cl_uint) \ + F(cl_mem_info, CL_MEM_REFERENCE_COUNT, cl_uint) \ + F(cl_mem_info, CL_MEM_CONTEXT, cl::Context) \ + \ + F(cl_image_info, CL_IMAGE_FORMAT, cl_image_format) \ + F(cl_image_info, CL_IMAGE_ELEMENT_SIZE, ::size_t) \ + F(cl_image_info, CL_IMAGE_ROW_PITCH, ::size_t) \ + F(cl_image_info, CL_IMAGE_SLICE_PITCH, ::size_t) \ + F(cl_image_info, CL_IMAGE_WIDTH, ::size_t) \ + F(cl_image_info, CL_IMAGE_HEIGHT, ::size_t) \ + F(cl_image_info, CL_IMAGE_DEPTH, ::size_t) \ + \ + F(cl_sampler_info, CL_SAMPLER_REFERENCE_COUNT, cl_uint) \ + F(cl_sampler_info, CL_SAMPLER_CONTEXT, cl::Context) \ + F(cl_sampler_info, CL_SAMPLER_NORMALIZED_COORDS, cl_addressing_mode) \ + F(cl_sampler_info, CL_SAMPLER_ADDRESSING_MODE, cl_filter_mode) \ + F(cl_sampler_info, CL_SAMPLER_FILTER_MODE, cl_bool) \ + \ + F(cl_program_info, CL_PROGRAM_REFERENCE_COUNT, cl_uint) \ + F(cl_program_info, CL_PROGRAM_CONTEXT, cl::Context) \ + F(cl_program_info, CL_PROGRAM_NUM_DEVICES, cl_uint) \ + F(cl_program_info, CL_PROGRAM_DEVICES, VECTOR_CLASS) \ + F(cl_program_info, CL_PROGRAM_SOURCE, STRING_CLASS) \ + F(cl_program_info, CL_PROGRAM_BINARY_SIZES, VECTOR_CLASS< ::size_t>) \ + F(cl_program_info, CL_PROGRAM_BINARIES, VECTOR_CLASS) \ + \ + F(cl_program_build_info, CL_PROGRAM_BUILD_STATUS, cl_build_status) \ + F(cl_program_build_info, CL_PROGRAM_BUILD_OPTIONS, STRING_CLASS) \ + F(cl_program_build_info, CL_PROGRAM_BUILD_LOG, STRING_CLASS) \ + \ + F(cl_kernel_info, CL_KERNEL_FUNCTION_NAME, STRING_CLASS) \ + F(cl_kernel_info, CL_KERNEL_NUM_ARGS, cl_uint) \ + F(cl_kernel_info, CL_KERNEL_REFERENCE_COUNT, cl_uint) \ + F(cl_kernel_info, CL_KERNEL_CONTEXT, cl::Context) \ + F(cl_kernel_info, CL_KERNEL_PROGRAM, cl::Program) \ + \ + F(cl_kernel_work_group_info, CL_KERNEL_WORK_GROUP_SIZE, ::size_t) \ + F(cl_kernel_work_group_info, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, cl::size_t<3>) \ + F(cl_kernel_work_group_info, CL_KERNEL_LOCAL_MEM_SIZE, cl_ulong) \ + \ + F(cl_command_queue_info, CL_QUEUE_CONTEXT, cl::Context) \ + F(cl_command_queue_info, CL_QUEUE_DEVICE, cl::Device) \ + F(cl_command_queue_info, CL_QUEUE_REFERENCE_COUNT, cl_uint) \ + F(cl_command_queue_info, CL_QUEUE_PROPERTIES, cl_command_queue_properties) + +#if defined(CL_VERSION_1_1) +#define __PARAM_NAME_INFO_1_1(F) \ + F(cl_context_info, CL_CONTEXT_NUM_DEVICES, cl_uint)\ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, cl_uint) \ + F(cl_device_info, CL_DEVICE_DOUBLE_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_HALF_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool) \ + \ + F(cl_mem_info, CL_MEM_ASSOCIATED_MEMOBJECT, cl::Memory) \ + F(cl_mem_info, CL_MEM_OFFSET, ::size_t) \ + \ + F(cl_kernel_work_group_info, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, ::size_t) \ + F(cl_kernel_work_group_info, CL_KERNEL_PRIVATE_MEM_SIZE, cl_ulong) \ + \ + F(cl_event_info, CL_EVENT_CONTEXT, cl::Context) +#endif // CL_VERSION_1_1 + +#if defined(USE_CL_DEVICE_FISSION) +#define __PARAM_NAME_DEVICE_FISSION(F) \ + F(cl_device_info, CL_DEVICE_PARENT_DEVICE_EXT, cl_device_id) \ + F(cl_device_info, CL_DEVICE_PARTITION_TYPES_EXT, VECTOR_CLASS) \ + F(cl_device_info, CL_DEVICE_AFFINITY_DOMAINS_EXT, VECTOR_CLASS) \ + F(cl_device_info, CL_DEVICE_REFERENCE_COUNT_EXT , cl_uint) \ + F(cl_device_info, CL_DEVICE_PARTITION_STYLE_EXT, VECTOR_CLASS) +#endif // USE_CL_DEVICE_FISSION + +template +struct param_traits {}; + +#define __DECLARE_PARAM_TRAITS(token, param_name, T) \ +struct token; \ +template<> \ +struct param_traits \ +{ \ + enum { value = param_name }; \ + typedef T param_type; \ +}; + +__PARAM_NAME_INFO_1_0(__DECLARE_PARAM_TRAITS) +#if defined(CL_VERSION_1_1) +__PARAM_NAME_INFO_1_1(__DECLARE_PARAM_TRAITS) +#endif // CL_VERSION_1_1 + +#if defined(USE_CL_DEVICE_FISSION) +__PARAM_NAME_DEVICE_FISSION(__DECLARE_PARAM_TRAITS); +#endif // USE_CL_DEVICE_FISSION + +#undef __DECLARE_PARAM_TRAITS + +// Convenience functions + +template +inline cl_int +getInfo(Func f, cl_uint name, T* param) +{ + return GetInfoHelper::get(f, name, param); +} + +template +struct GetInfoFunctor0 +{ + Func f_; const Arg0& arg0_; + cl_int operator ()( + cl_uint param, ::size_t size, void* value, ::size_t* size_ret) + { return f_(arg0_, param, size, value, size_ret); } +}; + +template +struct GetInfoFunctor1 +{ + Func f_; const Arg0& arg0_; const Arg1& arg1_; + cl_int operator ()( + cl_uint param, ::size_t size, void* value, ::size_t* size_ret) + { return f_(arg0_, arg1_, param, size, value, size_ret); } +}; + +template +inline cl_int +getInfo(Func f, const Arg0& arg0, cl_uint name, T* param) +{ + GetInfoFunctor0 f0 = { f, arg0 }; + return GetInfoHelper, T> + ::get(f0, name, param); +} + +template +inline cl_int +getInfo(Func f, const Arg0& arg0, const Arg1& arg1, cl_uint name, T* param) +{ + GetInfoFunctor1 f0 = { f, arg0, arg1 }; + return GetInfoHelper, T> + ::get(f0, name, param); +} + +template +struct ReferenceHandler +{ }; + +template <> +struct ReferenceHandler +{ + // cl_device_id does not have retain(). + static cl_int retain(cl_device_id) + { return CL_INVALID_DEVICE; } + // cl_device_id does not have release(). + static cl_int release(cl_device_id) + { return CL_INVALID_DEVICE; } +}; + +template <> +struct ReferenceHandler +{ + // cl_platform_id does not have retain(). + static cl_int retain(cl_platform_id) + { return CL_INVALID_PLATFORM; } + // cl_platform_id does not have release(). + static cl_int release(cl_platform_id) + { return CL_INVALID_PLATFORM; } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_context context) + { return ::clRetainContext(context); } + static cl_int release(cl_context context) + { return ::clReleaseContext(context); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_command_queue queue) + { return ::clRetainCommandQueue(queue); } + static cl_int release(cl_command_queue queue) + { return ::clReleaseCommandQueue(queue); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_mem memory) + { return ::clRetainMemObject(memory); } + static cl_int release(cl_mem memory) + { return ::clReleaseMemObject(memory); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_sampler sampler) + { return ::clRetainSampler(sampler); } + static cl_int release(cl_sampler sampler) + { return ::clReleaseSampler(sampler); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_program program) + { return ::clRetainProgram(program); } + static cl_int release(cl_program program) + { return ::clReleaseProgram(program); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_kernel kernel) + { return ::clRetainKernel(kernel); } + static cl_int release(cl_kernel kernel) + { return ::clReleaseKernel(kernel); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_event event) + { return ::clRetainEvent(event); } + static cl_int release(cl_event event) + { return ::clReleaseEvent(event); } +}; + +template +class Wrapper +{ +public: + typedef T cl_type; + +protected: + cl_type object_; + +public: + Wrapper() : object_(NULL) { } + + ~Wrapper() + { + if (object_ != NULL) { release(); } + } + + Wrapper(const Wrapper& rhs) + { + object_ = rhs.object_; + if (object_ != NULL) { retain(); } + } + + Wrapper& operator = (const Wrapper& rhs) + { + if (object_ != NULL) { release(); } + object_ = rhs.object_; + if (object_ != NULL) { retain(); } + return *this; + } + + cl_type operator ()() const { return object_; } + + cl_type& operator ()() { return object_; } + +protected: + + cl_int retain() const + { + return ReferenceHandler::retain(object_); + } + + cl_int release() const + { + return ReferenceHandler::release(object_); + } +}; + +#if defined(__CL_ENABLE_EXCEPTIONS) +static inline cl_int errHandler ( + cl_int err, + const char * errStr = NULL) throw(Error) +{ + if (err != CL_SUCCESS) { + throw Error(err, errStr); + } + return err; +} +#else +static inline cl_int errHandler (cl_int err, const char * errStr = NULL) +{ + return err; +} +#endif // __CL_ENABLE_EXCEPTIONS + +} // namespace detail +//! \endcond + +/*! \stuct ImageFormat + * \brief ImageFormat interface fro cl_image_format. + */ +struct ImageFormat : public cl_image_format +{ + ImageFormat(){} + + ImageFormat(cl_channel_order order, cl_channel_type type) + { + image_channel_order = order; + image_channel_data_type = type; + } + + ImageFormat& operator = (const ImageFormat& rhs) + { + if (this != &rhs) { + this->image_channel_data_type = rhs.image_channel_data_type; + this->image_channel_order = rhs.image_channel_order; + } + return *this; + } +}; + +/*! \class Device + * \brief Device interface for cl_device_id. + */ +class Device : public detail::Wrapper +{ +public: + Device(cl_device_id device) { object_ = device; } + + Device() : detail::Wrapper() { } + + Device(const Device& device) : detail::Wrapper(device) { } + + Device& operator = (const Device& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_device_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetDeviceInfo, object_, name, param), + __GET_DEVICE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_device_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + +#if defined(USE_CL_DEVICE_FISSION) + cl_int createSubDevices( + const cl_device_partition_property_ext * properties, + VECTOR_CLASS* devices) + { + typedef CL_API_ENTRY cl_int + ( CL_API_CALL * PFN_clCreateSubDevicesEXT)( + cl_device_id /*in_device*/, + const cl_device_partition_property_ext * /* properties */, + cl_uint /*num_entries*/, + cl_device_id * /*out_devices*/, + cl_uint * /*num_devices*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = NULL; + __INIT_CL_EXT_FCN_PTR(clCreateSubDevicesEXT); + + cl_uint n = 0; + cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_SUB_DEVICES); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_SUB_DEVICES); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +#endif +}; + +/*! \class Platform + * \brief Platform interface. + */ +class Platform : public detail::Wrapper +{ +public: + static const Platform null(); + + Platform(cl_platform_id platform) { object_ = platform; } + + Platform() : detail::Wrapper() { } + + Platform(const Platform& platform) : detail::Wrapper(platform) { } + + Platform& operator = (const Platform& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + cl_int getInfo(cl_platform_info name, STRING_CLASS* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetPlatformInfo, object_, name, param), + __GET_PLATFORM_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_platform_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int getDevices( + cl_device_type type, + VECTOR_CLASS* devices) const + { + cl_uint n = 0; + cl_int err = ::clGetDeviceIDs(object_, type, 0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = ::clGetDeviceIDs(object_, type, n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } + +#if defined(USE_DX_INTEROP) + /*! \brief Get the list of available D3D10 devices. + * + * \param d3d_device_source. + * + * \param d3d_object. + * + * \param d3d_device_set. + * + * \param devices returns a vector of OpenCL D3D10 devices found. The cl::Device + * values returned in devices can be used to identify a specific OpenCL + * device. If \a devices argument is NULL, this argument is ignored. + * + * \return One of the following values: + * - CL_SUCCESS if the function is executed successfully. + * + * The application can query specific capabilities of the OpenCL device(s) + * returned by cl::getDevices. This can be used by the application to + * determine which device(s) to use. + * + * \note In the case that exceptions are enabled and a return value + * other than CL_SUCCESS is generated, then cl::Error exception is + * generated. + */ + cl_int getDevices( + cl_d3d10_device_source_khr d3d_device_source, + void * d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + VECTOR_CLASS* devices) const + { + typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clGetDeviceIDsFromD3D10KHR)( + cl_platform_id platform, + cl_d3d10_device_source_khr d3d_device_source, + void * d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id * devices, + cl_uint* num_devices); + + static PFN_clGetDeviceIDsFromD3D10KHR pfn_clGetDeviceIDsFromD3D10KHR = NULL; + __INIT_CL_EXT_FCN_PTR(clGetDeviceIDsFromD3D10KHR); + + cl_uint n = 0; + cl_int err = pfn_clGetDeviceIDsFromD3D10KHR( + object_, + d3d_device_source, + d3d_object, + d3d_device_set, + 0, + NULL, + &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = pfn_clGetDeviceIDsFromD3D10KHR( + object_, + d3d_device_source, + d3d_object, + d3d_device_set, + n, + ids, + NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +#endif + + static cl_int get( + VECTOR_CLASS* platforms) + { + cl_uint n = 0; + cl_int err = ::clGetPlatformIDs(0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); + } + + cl_platform_id* ids = (cl_platform_id*) alloca( + n * sizeof(cl_platform_id)); + err = ::clGetPlatformIDs(n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); + } + + platforms->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +}; + +static inline cl_int +UnloadCompiler() +{ + return ::clUnloadCompiler(); +} + +class Context : public detail::Wrapper +{ +public: + Context( + const VECTOR_CLASS& devices, + cl_context_properties* properties = NULL, + void (CL_CALLBACK * notifyFptr)( + const char *, + const void *, + ::size_t, + void *) = NULL, + void* data = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateContext( + properties, (cl_uint) devices.size(), + (cl_device_id*) &devices.front(), + notifyFptr, data, &error); + + detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); + if (err != NULL) { + *err = error; + } + } + + Context( + cl_device_type type, + cl_context_properties* properties = NULL, + void (CL_CALLBACK * notifyFptr)( + const char *, + const void *, + ::size_t, + void *) = NULL, + void* data = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateContextFromType( + properties, type, notifyFptr, data, &error); + + detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); + if (err != NULL) { + *err = error; + } + } + + Context() : detail::Wrapper() { } + + Context(const Context& context) : detail::Wrapper(context) { } + + Context& operator = (const Context& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_context_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetContextInfo, object_, name, param), + __GET_CONTEXT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_context_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int getSupportedImageFormats( + cl_mem_flags flags, + cl_mem_object_type type, + VECTOR_CLASS* formats) const + { + cl_uint numEntries; + cl_int err = ::clGetSupportedImageFormats( + object_, + flags, + type, + 0, + NULL, + &numEntries); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); + } + + ImageFormat* value = (ImageFormat*) + alloca(numEntries * sizeof(ImageFormat)); + err = ::clGetSupportedImageFormats( + object_, + flags, + type, + numEntries, + (cl_image_format*) value, + NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); + } + + formats->assign(&value[0], &value[numEntries]); + return CL_SUCCESS; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Context) + +/*! \class Event + * \brief Event interface for cl_event. + */ +class Event : public detail::Wrapper +{ +public: + Event() : detail::Wrapper() { } + + Event(const Event& event) : detail::Wrapper(event) { } + + Event& operator = (const Event& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_event_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetEventInfo, object_, name, param), + __GET_EVENT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_event_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getProfilingInfo(cl_profiling_info name, T* param) const + { + return detail::errHandler(detail::getInfo( + &::clGetEventProfilingInfo, object_, name, param), + __GET_EVENT_PROFILE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getProfilingInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_profiling_info, name>::param_type param; + cl_int result = getProfilingInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int wait() const + { + return detail::errHandler( + ::clWaitForEvents(1, &object_), + __WAIT_FOR_EVENTS_ERR); + } + +#if defined(CL_VERSION_1_1) + cl_int setCallback( + cl_int type, + void (CL_CALLBACK * pfn_notify)(cl_event, cl_int, void *), + void * user_data = NULL) + { + return detail::errHandler( + ::clSetEventCallback( + object_, + type, + pfn_notify, + user_data), + __SET_EVENT_CALLBACK_ERR); + } +#endif + + static cl_int + waitForEvents(const VECTOR_CLASS& events) + { + return detail::errHandler( + ::clWaitForEvents( + (cl_uint) events.size(), (cl_event*)&events.front()), + __WAIT_FOR_EVENTS_ERR); + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Event) + +#if defined(CL_VERSION_1_1) +/*! \class UserEvent + * \brief User event interface for cl_event. + */ +class UserEvent : public Event +{ +public: + UserEvent( + const Context& context, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateUserEvent( + context(), + &error); + + detail::errHandler(error, __CREATE_USER_EVENT_ERR); + if (err != NULL) { + *err = error; + } + } + + UserEvent() : Event() { } + + UserEvent(const UserEvent& event) : Event(event) { } + + UserEvent& operator = (const UserEvent& rhs) + { + if (this != &rhs) { + Event::operator=(rhs); + } + return *this; + } + + cl_int setStatus(cl_int status) + { + return detail::errHandler( + ::clSetUserEventStatus(object_,status), + __SET_USER_EVENT_STATUS_ERR); + } +}; +#endif + +inline static cl_int +WaitForEvents(const VECTOR_CLASS& events) +{ + return detail::errHandler( + ::clWaitForEvents( + (cl_uint) events.size(), (cl_event*)&events.front()), + __WAIT_FOR_EVENTS_ERR); +} + +/*! \class Memory + * \brief Memory interface for cl_mem. + */ +class Memory : public detail::Wrapper +{ +public: + Memory() : detail::Wrapper() { } + + Memory(const Memory& memory) : detail::Wrapper(memory) { } + + Memory& operator = (const Memory& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_mem_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetMemObjectInfo, object_, name, param), + __GET_MEM_OBJECT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_mem_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + +#if defined(CL_VERSION_1_1) + cl_int setDestructorCallback( + void (CL_CALLBACK * pfn_notify)(cl_mem, void *), + void * user_data = NULL) + { + return detail::errHandler( + ::clSetMemObjectDestructorCallback( + object_, + pfn_notify, + user_data), + __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR); + } +#endif + +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Memory) + +/*! \class Buffer + * \brief Memory buffer interface. + */ +class Buffer : public Memory +{ +public: + Buffer( + const Context& context, + cl_mem_flags flags, + ::size_t size, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); + + detail::errHandler(error, __CREATE_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Buffer() : Memory() { } + + Buffer(const Buffer& buffer) : Memory(buffer) { } + + Buffer& operator = (const Buffer& rhs) + { + if (this != &rhs) { + Memory::operator=(rhs); + } + return *this; + } + +#if defined(CL_VERSION_1_1) + Buffer createSubBuffer( + cl_mem_flags flags, + cl_buffer_create_type buffer_create_type, + const void * buffer_create_info, + cl_int * err = NULL) + { + Buffer result; + cl_int error; + result.object_ = ::clCreateSubBuffer( + object_, + flags, + buffer_create_type, + buffer_create_info, + &error); + + detail::errHandler(error, __CREATE_SUBBUFFER_ERR); + if (err != NULL) { + *err = error; + } + + return result; + } +#endif +}; + +#if defined (USE_DX_INTEROP) +class BufferD3D10 : public Buffer +{ +public: + typedef CL_API_ENTRY cl_mem (CL_API_CALL *PFN_clCreateFromD3D10BufferKHR)( + cl_context context, cl_mem_flags flags, ID3D10Buffer* buffer, + cl_int* errcode_ret); + + BufferD3D10( + const Context& context, + cl_mem_flags flags, + ID3D10Buffer* bufobj, + cl_int * err = NULL) + { + static PFN_clCreateFromD3D10BufferKHR pfn_clCreateFromD3D10BufferKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clCreateFromD3D10BufferKHR); + + cl_int error; + object_ = pfn_clCreateFromD3D10BufferKHR( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferD3D10() : Buffer() { } + + BufferD3D10(const BufferD3D10& buffer) : Buffer(buffer) { } + + BufferD3D10& operator = (const BufferD3D10& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } +}; +#endif + +/*! \class BufferGL + * \brief Memory buffer interface for GL interop. + */ +class BufferGL : public Buffer +{ +public: + BufferGL( + const Context& context, + cl_mem_flags flags, + GLuint bufobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLBuffer( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferGL() : Buffer() { } + + BufferGL(const BufferGL& buffer) : Buffer(buffer) { } + + BufferGL& operator = (const BufferGL& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } + + cl_int getObjectInfo( + cl_gl_object_type *type, + GLuint * gl_object_name) + { + return detail::errHandler( + ::clGetGLObjectInfo(object_,type,gl_object_name), + __GET_GL_OBJECT_INFO_ERR); + } +}; + +/*! \class BufferRenderGL + * \brief Memory buffer interface for GL interop with renderbuffer. + */ +class BufferRenderGL : public Buffer +{ +public: + BufferRenderGL( + const Context& context, + cl_mem_flags flags, + GLuint bufobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLRenderbuffer( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferRenderGL() : Buffer() { } + + BufferRenderGL(const BufferGL& buffer) : Buffer(buffer) { } + + BufferRenderGL& operator = (const BufferRenderGL& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } + + cl_int getObjectInfo( + cl_gl_object_type *type, + GLuint * gl_object_name) + { + return detail::errHandler( + ::clGetGLObjectInfo(object_,type,gl_object_name), + __GET_GL_OBJECT_INFO_ERR); + } +}; + +/*! \class Image + * \brief Base class interface for all images. + */ +class Image : public Memory +{ +protected: + Image() : Memory() { } + + Image(const Image& image) : Memory(image) { } + + Image& operator = (const Image& rhs) + { + if (this != &rhs) { + Memory::operator=(rhs); + } + return *this; + } +public: + template + cl_int getImageInfo(cl_image_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetImageInfo, object_, name, param), + __GET_IMAGE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getImageInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_image_info, name>::param_type param; + cl_int result = getImageInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } +}; + +/*! \class Image2D + * \brief Image interface for 2D images. + */ +class Image2D : public Image +{ +public: + Image2D( + const Context& context, + cl_mem_flags flags, + ImageFormat format, + ::size_t width, + ::size_t height, + ::size_t row_pitch = 0, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateImage2D( + context(), flags,&format, width, height, row_pitch, host_ptr, &error); + + detail::errHandler(error, __CREATE_IMAGE2D_ERR); + if (err != NULL) { + *err = error; + } + } + + Image2D() { } + + Image2D(const Image2D& image2D) : Image(image2D) { } + + Image2D& operator = (const Image2D& rhs) + { + if (this != &rhs) { + Image::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image2DGL + * \brief 2D image interface for GL interop. + */ +class Image2DGL : public Image2D +{ +public: + Image2DGL( + const Context& context, + cl_mem_flags flags, + GLenum target, + GLint miplevel, + GLuint texobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLTexture2D( + context(), + flags, + target, + miplevel, + texobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Image2DGL() : Image2D() { } + + Image2DGL(const Image2DGL& image) : Image2D(image) { } + + Image2DGL& operator = (const Image2DGL& rhs) + { + if (this != &rhs) { + Image2D::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image3D + * \brief Image interface for 3D images. + */ +class Image3D : public Image +{ +public: + Image3D( + const Context& context, + cl_mem_flags flags, + ImageFormat format, + ::size_t width, + ::size_t height, + ::size_t depth, + ::size_t row_pitch = 0, + ::size_t slice_pitch = 0, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateImage3D( + context(), flags, &format, width, height, depth, row_pitch, + slice_pitch, host_ptr, &error); + + detail::errHandler(error, __CREATE_IMAGE3D_ERR); + if (err != NULL) { + *err = error; + } + } + + Image3D() { } + + Image3D(const Image3D& image3D) : Image(image3D) { } + + Image3D& operator = (const Image3D& rhs) + { + if (this != &rhs) { + Image::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image2DGL + * \brief 2D image interface for GL interop. + */ +class Image3DGL : public Image3D +{ +public: + Image3DGL( + const Context& context, + cl_mem_flags flags, + GLenum target, + GLint miplevel, + GLuint texobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLTexture3D( + context(), + flags, + target, + miplevel, + texobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Image3DGL() : Image3D() { } + + Image3DGL(const Image3DGL& image) : Image3D(image) { } + + Image3DGL& operator = (const Image3DGL& rhs) + { + if (this != &rhs) { + Image3D::operator=(rhs); + } + return *this; + } +}; + +/*! \class Sampler + * \brief Sampler interface for cl_sampler. + */ +class Sampler : public detail::Wrapper +{ +public: + Sampler() { } + + Sampler( + const Context& context, + cl_bool normalized_coords, + cl_addressing_mode addressing_mode, + cl_filter_mode filter_mode, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateSampler( + context(), + normalized_coords, + addressing_mode, + filter_mode, + &error); + + detail::errHandler(error, __CREATE_SAMPLER_ERR); + if (err != NULL) { + *err = error; + } + } + + Sampler(const Sampler& sampler) : detail::Wrapper(sampler) { } + + Sampler& operator = (const Sampler& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_sampler_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetSamplerInfo, object_, name, param), + __GET_SAMPLER_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_sampler_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Sampler) + +class Program; +class CommandQueue; +class Kernel; + +/*! \class NDRange + * \brief NDRange interface + */ +class NDRange +{ +private: + size_t<3> sizes_; + cl_uint dimensions_; + +public: + NDRange() + : dimensions_(0) + { } + + NDRange(::size_t size0) + : dimensions_(1) + { + sizes_.push_back(size0); + } + + NDRange(::size_t size0, ::size_t size1) + : dimensions_(2) + { + sizes_.push_back(size0); + sizes_.push_back(size1); + } + + NDRange(::size_t size0, ::size_t size1, ::size_t size2) + : dimensions_(3) + { + sizes_.push_back(size0); + sizes_.push_back(size1); + sizes_.push_back(size2); + } + + operator const ::size_t*() const { return (const ::size_t*) sizes_; } + ::size_t dimensions() const { return dimensions_; } +}; + +static const NDRange NullRange; + +/*! + * \struct LocalSpaceArg + * \brief Local address raper for use with Kernel::setArg + */ +struct LocalSpaceArg +{ + ::size_t size_; +}; + +namespace detail { + +template +struct KernelArgumentHandler +{ + static ::size_t size(const T&) { return sizeof(T); } + static T* ptr(T& value) { return &value; } +}; + +template <> +struct KernelArgumentHandler +{ + static ::size_t size(const LocalSpaceArg& value) { return value.size_; } + static void* ptr(LocalSpaceArg&) { return NULL; } +}; + +} +//! \endcond + +inline LocalSpaceArg +__local(::size_t size) +{ + LocalSpaceArg ret = { size }; + return ret; +} + +class KernelFunctor; + +/*! \class Kernel + * \brief Kernel interface that implements cl_kernel + */ +class Kernel : public detail::Wrapper +{ +public: + inline Kernel(const Program& program, const char* name, cl_int* err = NULL); + + Kernel() { } + + Kernel(const Kernel& kernel) : detail::Wrapper(kernel) { } + + Kernel& operator = (const Kernel& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_kernel_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetKernelInfo, object_, name, param), + __GET_KERNEL_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_kernel_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getWorkGroupInfo( + const Device& device, cl_kernel_work_group_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetKernelWorkGroupInfo, object_, device(), name, param), + __GET_KERNEL_WORK_GROUP_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getWorkGroupInfo(const Device& device, cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_kernel_work_group_info, name>::param_type param; + cl_int result = getWorkGroupInfo(device, name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int setArg(cl_uint index, T value) + { + return detail::errHandler( + ::clSetKernelArg( + object_, + index, + detail::KernelArgumentHandler::size(value), + detail::KernelArgumentHandler::ptr(value)), + __SET_KERNEL_ARGS_ERR); + } + + cl_int setArg(cl_uint index, ::size_t size, void* argPtr) + { + return detail::errHandler( + ::clSetKernelArg(object_, index, size, argPtr), + __SET_KERNEL_ARGS_ERR); + } + + KernelFunctor bind( + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local); + + KernelFunctor bind( + const CommandQueue& queue, + const NDRange& global, + const NDRange& local); +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Kernel) + +/*! \class Program + * \brief Program interface that implements cl_program. + */ +class Program : public detail::Wrapper +{ +public: + typedef VECTOR_CLASS > Binaries; + typedef VECTOR_CLASS > Sources; + + Program( + const Context& context, + const Sources& sources, + cl_int* err = NULL) + { + cl_int error; + + const ::size_t n = (::size_t)sources.size(); + ::size_t* lengths = (::size_t*) alloca(n * sizeof(::size_t)); + const char** strings = (const char**) alloca(n * sizeof(const char*)); + + for (::size_t i = 0; i < n; ++i) { + strings[i] = sources[(int)i].first; + lengths[i] = sources[(int)i].second; + } + + object_ = ::clCreateProgramWithSource( + context(), (cl_uint)n, strings, lengths, &error); + + detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); + if (err != NULL) { + *err = error; + } + } + + Program( + const Context& context, + const VECTOR_CLASS& devices, + const Binaries& binaries, + VECTOR_CLASS* binaryStatus = NULL, + cl_int* err = NULL) + { + cl_int error; + const ::size_t n = binaries.size(); + ::size_t* lengths = (::size_t*) alloca(n * sizeof(::size_t)); + const unsigned char** images = (const unsigned char**) alloca(n * sizeof(const void*)); + + for (::size_t i = 0; i < n; ++i) { + images[i] = (const unsigned char*)binaries[(int)i].first; + lengths[i] = binaries[(int)i].second; + } + + object_ = ::clCreateProgramWithBinary( + context(), (cl_uint) devices.size(), + (cl_device_id*)&devices.front(), + lengths, images, binaryStatus != NULL + ? (cl_int*) &binaryStatus->front() + : NULL, &error); + + detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); + if (err != NULL) { + *err = error; + } + } + + Program() { } + + Program(const Program& program) : detail::Wrapper(program) { } + + Program& operator = (const Program& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + cl_int build( + const VECTOR_CLASS& devices, + const char* options = NULL, + void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, + void* data = NULL) const + { + return detail::errHandler( + ::clBuildProgram( + object_, + (cl_uint) + devices.size(), + (cl_device_id*)&devices.front(), + options, + notifyFptr, + data), + __BUILD_PROGRAM_ERR); + } + + template + cl_int getInfo(cl_program_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetProgramInfo, object_, name, param), + __GET_PROGRAM_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_program_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getBuildInfo( + const Device& device, cl_program_build_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetProgramBuildInfo, object_, device(), name, param), + __GET_PROGRAM_BUILD_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getBuildInfo(const Device& device, cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_program_build_info, name>::param_type param; + cl_int result = getBuildInfo(device, name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int createKernels(VECTOR_CLASS* kernels) + { + cl_uint numKernels; + cl_int err = ::clCreateKernelsInProgram(object_, 0, NULL, &numKernels); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); + } + + Kernel* value = (Kernel*) alloca(numKernels * sizeof(Kernel)); + err = ::clCreateKernelsInProgram( + object_, numKernels, (cl_kernel*) value, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); + } + + kernels->assign(&value[0], &value[numKernels]); + return CL_SUCCESS; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Program) + +inline Kernel::Kernel(const Program& program, const char* name, cl_int* err) +{ + cl_int error; + + object_ = ::clCreateKernel(program(), name, &error); + detail::errHandler(error, __CREATE_KERNEL_ERR); + + if (err != NULL) { + *err = error; + } + +} + +/*! \class CommandQueue + * \brief CommandQueue interface for cl_command_queue. + */ +class CommandQueue : public detail::Wrapper +{ +public: + CommandQueue( + const Context& context, + const Device& device, + cl_command_queue_properties properties = 0, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateCommandQueue( + context(), device(), properties, &error); + + detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); + if (err != NULL) { + *err = error; + } + } + + CommandQueue() { } + + CommandQueue(const CommandQueue& commandQueue) : detail::Wrapper(commandQueue) { } + + CommandQueue& operator = (const CommandQueue& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_command_queue_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetCommandQueueInfo, object_, name, param), + __GET_COMMAND_QUEUE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_command_queue_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int enqueueReadBuffer( + const Buffer& buffer, + cl_bool blocking, + ::size_t offset, + ::size_t size, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadBuffer( + object_, buffer(), blocking, offset, size, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_BUFFER_ERR); + } + + cl_int enqueueWriteBuffer( + const Buffer& buffer, + cl_bool blocking, + ::size_t offset, + ::size_t size, + const void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteBuffer( + object_, buffer(), blocking, offset, size, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_BUFFER_ERR); + } + + cl_int enqueueCopyBuffer( + const Buffer& src, + const Buffer& dst, + ::size_t src_offset, + ::size_t dst_offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBuffer( + object_, src(), dst(), src_offset, dst_offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQEUE_COPY_BUFFER_ERR); + } + +#if defined(CL_VERSION_1_1) + cl_int enqueueReadBufferRect( + const Buffer& buffer, + cl_bool blocking, + const size_t<3>& buffer_offset, + const size_t<3>& host_offset, + const size_t<3>& region, + ::size_t buffer_row_pitch, + ::size_t buffer_slice_pitch, + ::size_t host_row_pitch, + ::size_t host_slice_pitch, + void *ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadBufferRect( + object_, + buffer(), + blocking, + (const ::size_t *)buffer_offset, + (const ::size_t *)host_offset, + (const ::size_t *)region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_BUFFER_RECT_ERR); + } + + + cl_int enqueueWriteBufferRect( + const Buffer& buffer, + cl_bool blocking, + const size_t<3>& buffer_offset, + const size_t<3>& host_offset, + const size_t<3>& region, + ::size_t buffer_row_pitch, + ::size_t buffer_slice_pitch, + ::size_t host_row_pitch, + ::size_t host_slice_pitch, + void *ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteBufferRect( + object_, + buffer(), + blocking, + (const ::size_t *)buffer_offset, + (const ::size_t *)host_offset, + (const ::size_t *)region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_BUFFER_RECT_ERR); + } + + cl_int enqueueCopyBufferRect( + const Buffer& src, + const Buffer& dst, + const size_t<3>& src_origin, + const size_t<3>& dst_origin, + const size_t<3>& region, + ::size_t src_row_pitch, + ::size_t src_slice_pitch, + ::size_t dst_row_pitch, + ::size_t dst_slice_pitch, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBufferRect( + object_, + src(), + dst(), + (const ::size_t *)src_origin, + (const ::size_t *)dst_origin, + (const ::size_t *)region, + src_row_pitch, + src_slice_pitch, + dst_row_pitch, + dst_slice_pitch, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQEUE_COPY_BUFFER_RECT_ERR); + } +#endif + + cl_int enqueueReadImage( + const Image& image, + cl_bool blocking, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t row_pitch, + ::size_t slice_pitch, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadImage( + object_, image(), blocking, (const ::size_t *) origin, + (const ::size_t *) region, row_pitch, slice_pitch, ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_IMAGE_ERR); + } + + cl_int enqueueWriteImage( + const Image& image, + cl_bool blocking, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t row_pitch, + ::size_t slice_pitch, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteImage( + object_, image(), blocking, (const ::size_t *) origin, + (const ::size_t *) region, row_pitch, slice_pitch, ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_IMAGE_ERR); + } + + cl_int enqueueCopyImage( + const Image& src, + const Image& dst, + const size_t<3>& src_origin, + const size_t<3>& dst_origin, + const size_t<3>& region, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyImage( + object_, src(), dst(), (const ::size_t *) src_origin, + (const ::size_t *)dst_origin, (const ::size_t *) region, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_IMAGE_ERR); + } + + cl_int enqueueCopyImageToBuffer( + const Image& src, + const Buffer& dst, + const size_t<3>& src_origin, + const size_t<3>& region, + ::size_t dst_offset, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyImageToBuffer( + object_, src(), dst(), (const ::size_t *) src_origin, + (const ::size_t *) region, dst_offset, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR); + } + + cl_int enqueueCopyBufferToImage( + const Buffer& src, + const Image& dst, + ::size_t src_offset, + const size_t<3>& dst_origin, + const size_t<3>& region, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBufferToImage( + object_, src(), dst(), src_offset, + (const ::size_t *) dst_origin, (const ::size_t *) region, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR); + } + + void* enqueueMapBuffer( + const Buffer& buffer, + cl_bool blocking, + cl_map_flags flags, + ::size_t offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL, + cl_int* err = NULL) const + { + cl_int error; + void * result = ::clEnqueueMapBuffer( + object_, buffer(), blocking, flags, offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event, + &error); + + detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + return result; + } + + void* enqueueMapImage( + const Image& buffer, + cl_bool blocking, + cl_map_flags flags, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t * row_pitch, + ::size_t * slice_pitch, + const VECTOR_CLASS* events = NULL, + Event* event = NULL, + cl_int* err = NULL) const + { + cl_int error; + void * result = ::clEnqueueMapImage( + object_, buffer(), blocking, flags, + (const ::size_t *) origin, (const ::size_t *) region, + row_pitch, slice_pitch, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event, + &error); + + detail::errHandler(error, __ENQUEUE_MAP_IMAGE_ERR); + if (err != NULL) { + *err = error; + } + return result; + } + + cl_int enqueueUnmapMemObject( + const Memory& memory, + void* mapped_ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueUnmapMemObject( + object_, memory(), mapped_ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_UNMAP_MEM_OBJECT_ERR); + } + + cl_int enqueueNDRangeKernel( + const Kernel& kernel, + const NDRange& offset, + const NDRange& global, + const NDRange& local, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueNDRangeKernel( + object_, kernel(), (cl_uint) global.dimensions(), + offset.dimensions() != 0 ? (const ::size_t*) offset : NULL, + (const ::size_t*) global, + local.dimensions() != 0 ? (const ::size_t*) local : NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_NDRANGE_KERNEL_ERR); + } + + cl_int enqueueTask( + const Kernel& kernel, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueTask( + object_, kernel(), + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_TASK_ERR); + } + + cl_int enqueueNativeKernel( + void (*userFptr)(void *), + std::pair args, + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* mem_locs = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + cl_mem * mems = (mem_objects != NULL && mem_objects->size() > 0) + ? (cl_mem*) alloca(mem_objects->size() * sizeof(cl_mem)) + : NULL; + + if (mems != NULL) { + for (unsigned int i = 0; i < mem_objects->size(); i++) { + mems[i] = ((*mem_objects)[i])(); + } + } + + return detail::errHandler( + ::clEnqueueNativeKernel( + object_, userFptr, args.first, args.second, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + mems, + (mem_locs != NULL) ? (const void **) &mem_locs->front() : NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_NATIVE_KERNEL); + } + + cl_int enqueueMarker(Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueMarker(object_, (cl_event*) event), + __ENQUEUE_MARKER_ERR); + } + + cl_int enqueueWaitForEvents(const VECTOR_CLASS& events) const + { + return detail::errHandler( + ::clEnqueueWaitForEvents( + object_, + (cl_uint) events.size(), + (const cl_event*) &events.front()), + __ENQUEUE_WAIT_FOR_EVENTS_ERR); + } + + cl_int enqueueAcquireGLObjects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueAcquireGLObjects( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_ACQUIRE_GL_ERR); + } + + cl_int enqueueReleaseGLObjects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReleaseGLObjects( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_RELEASE_GL_ERR); + } + +#if defined (USE_DX_INTEROP) +typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueAcquireD3D10ObjectsKHR)( + cl_command_queue command_queue, cl_uint num_objects, + const cl_mem* mem_objects, cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, cl_event* event); +typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueReleaseD3D10ObjectsKHR)( + cl_command_queue command_queue, cl_uint num_objects, + const cl_mem* mem_objects, cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, cl_event* event); + + cl_int enqueueAcquireD3D10Objects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + static PFN_clEnqueueAcquireD3D10ObjectsKHR pfn_clEnqueueAcquireD3D10ObjectsKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clEnqueueAcquireD3D10ObjectsKHR); + + return detail::errHandler( + pfn_clEnqueueAcquireD3D10ObjectsKHR( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_ACQUIRE_GL_ERR); + } + + cl_int enqueueReleaseD3D10Objects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + static PFN_clEnqueueReleaseD3D10ObjectsKHR pfn_clEnqueueReleaseD3D10ObjectsKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clEnqueueReleaseD3D10ObjectsKHR); + + return detail::errHandler( + pfn_clEnqueueReleaseD3D10ObjectsKHR( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_RELEASE_GL_ERR); + } +#endif + + cl_int enqueueBarrier() const + { + return detail::errHandler( + ::clEnqueueBarrier(object_), + __ENQUEUE_BARRIER_ERR); + } + + cl_int flush() const + { + return detail::errHandler(::clFlush(object_), __FLUSH_ERR); + } + + cl_int finish() const + { + return detail::errHandler(::clFinish(object_), __FINISH_ERR); + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::CommandQueue) + +/*! \class KernelFunctor + * \brief Kernel functor interface + * + * \note Currently only functors of zero to ten arguments are supported. It + * is straightforward to add more and a more general solution, similar to + * Boost.Lambda could be followed if required in the future. + */ +class KernelFunctor +{ +private: + Kernel kernel_; + CommandQueue queue_; + NDRange offset_; + NDRange global_; + NDRange local_; + + cl_int err_; +public: + KernelFunctor() { } + + KernelFunctor( + const Kernel& kernel, + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local) : + kernel_(kernel), + queue_(queue), + offset_(offset), + global_(global), + local_(local), + err_(CL_SUCCESS) + {} + + KernelFunctor& operator=(const KernelFunctor& rhs); + + KernelFunctor(const KernelFunctor& rhs); + + cl_int getError() { return err_; } + + inline Event operator()(const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const A15& a15, + const VECTOR_CLASS* events = NULL); +}; + +inline KernelFunctor Kernel::bind( + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local) +{ + return KernelFunctor(*this,queue,offset,global,local); +} + +inline KernelFunctor Kernel::bind( + const CommandQueue& queue, + const NDRange& global, + const NDRange& local) +{ + return KernelFunctor(*this,queue,NullRange,global,local); +} + +inline KernelFunctor& KernelFunctor::operator=(const KernelFunctor& rhs) +{ + if (this == &rhs) { + return *this; + } + + kernel_ = rhs.kernel_; + queue_ = rhs.queue_; + offset_ = rhs.offset_; + global_ = rhs.global_; + local_ = rhs.local_; + + return *this; +} + +inline KernelFunctor::KernelFunctor(const KernelFunctor& rhs) : + kernel_(rhs.kernel_), + queue_(rhs.queue_), + offset_(rhs.offset_), + global_(rhs.global_), + local_(rhs.local_) +{ +} + +Event KernelFunctor::operator()(const VECTOR_CLASS* events) +{ + (void)events; + Event event; + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + kernel_.setArg(13,a14); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const A15& a15, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + kernel_.setArg(13,a14); + kernel_.setArg(14,a15); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +#undef __ERR_STR +#if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) +#undef __GET_DEVICE_INFO_ERR +#undef __GET_PLATFORM_INFO_ERR +#undef __GET_DEVICE_IDS_ERR +#undef __GET_CONTEXT_INFO_ERR +#undef __GET_EVENT_INFO_ERR +#undef __GET_EVENT_PROFILE_INFO_ERR +#undef __GET_MEM_OBJECT_INFO_ERR +#undef __GET_IMAGE_INFO_ERR +#undef __GET_SAMPLER_INFO_ERR +#undef __GET_KERNEL_INFO_ERR +#undef __GET_KERNEL_WORK_GROUP_INFO_ERR +#undef __GET_PROGRAM_INFO_ERR +#undef __GET_PROGRAM_BUILD_INFO_ERR +#undef __GET_COMMAND_QUEUE_INFO_ERR + +#undef __CREATE_CONTEXT_FROM_TYPE_ERR +#undef __GET_SUPPORTED_IMAGE_FORMATS_ERR + +#undef __CREATE_BUFFER_ERR +#undef __CREATE_SUBBUFFER_ERR +#undef __CREATE_IMAGE2D_ERR +#undef __CREATE_IMAGE3D_ERR +#undef __CREATE_SAMPLER_ERR +#undef __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR + +#undef __CREATE_USER_EVENT_ERR +#undef __SET_USER_EVENT_STATUS_ERR +#undef __SET_EVENT_CALLBACK_ERR + +#undef __WAIT_FOR_EVENTS_ERR + +#undef __CREATE_KERNEL_ERR +#undef __SET_KERNEL_ARGS_ERR +#undef __CREATE_PROGRAM_WITH_SOURCE_ERR +#undef __CREATE_PROGRAM_WITH_BINARY_ERR +#undef __BUILD_PROGRAM_ERR +#undef __CREATE_KERNELS_IN_PROGRAM_ERR + +#undef __CREATE_COMMAND_QUEUE_ERR +#undef __SET_COMMAND_QUEUE_PROPERTY_ERR +#undef __ENQUEUE_READ_BUFFER_ERR +#undef __ENQUEUE_WRITE_BUFFER_ERR +#undef __ENQUEUE_READ_BUFFER_RECT_ERR +#undef __ENQUEUE_WRITE_BUFFER_RECT_ERR +#undef __ENQEUE_COPY_BUFFER_ERR +#undef __ENQEUE_COPY_BUFFER_RECT_ERR +#undef __ENQUEUE_READ_IMAGE_ERR +#undef __ENQUEUE_WRITE_IMAGE_ERR +#undef __ENQUEUE_COPY_IMAGE_ERR +#undef __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR +#undef __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR +#undef __ENQUEUE_MAP_BUFFER_ERR +#undef __ENQUEUE_MAP_IMAGE_ERR +#undef __ENQUEUE_UNMAP_MEM_OBJECT_ERR +#undef __ENQUEUE_NDRANGE_KERNEL_ERR +#undef __ENQUEUE_TASK_ERR +#undef __ENQUEUE_NATIVE_KERNEL + +#undef __UNLOAD_COMPILER_ERR +#endif //__CL_USER_OVERRIDE_ERROR_STRINGS + +#undef __GET_INFO_HELPER_WITH_RETAIN + +// Extensions +#undef __INIT_CL_EXT_FCN_PTR +#undef __CREATE_SUB_DEVICES + +#if defined(USE_CL_DEVICE_FISSION) +#undef __PARAM_NAME_DEVICE_FISSION +#endif // USE_CL_DEVICE_FISSION + +} // namespace cl + +#endif // CL_HPP_ diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp new file mode 100644 index 000000000..96f1fa582 --- /dev/null +++ b/libethash-cl/ethash_cl_miner.cpp @@ -0,0 +1,332 @@ +/* + This file is part of c-ethash. + + c-ethash 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. + + c-ethash 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 ethash_cl_miner.cpp +* @author Tim Hughes +* @date 2015 +*/ + + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include +#include "ethash_cl_miner.h" +#include "ethash_cl_miner_kernel.h" + +#define ETHASH_BYTES 32 + +// workaround lame platforms +#if !CL_VERSION_1_2 +#define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE +#define CL_MEM_HOST_READ_ONLY 0 +#endif + +#undef min +#undef max + +static void add_definition(std::string& source, char const* id, unsigned value) +{ + char buf[256]; + sprintf(buf, "#define %s %uu\n", id, value); + source.insert(source.begin(), buf, buf + strlen(buf)); +} + +ethash_cl_miner::ethash_cl_miner() +: m_opencl_1_1() +{ +} + +void ethash_cl_miner::finish() +{ + if (m_queue()) + { + m_queue.finish(); + } +} + +bool ethash_cl_miner::init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size) +{ + // store params + m_params = params; + + // get all platforms + std::vector platforms; + cl::Platform::get(&platforms); + if (platforms.empty()) + { + debugf("No OpenCL platforms found.\n"); + return false; + } + + // use default platform + fprintf(stderr, "Using platform: %s\n", platforms[0].getInfo().c_str()); + + // get GPU device of the default platform + std::vector devices; + platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices); + if (devices.empty()) + { + debugf("No OpenCL devices found.\n"); + return false; + } + + // use default device + unsigned device_num = 0; + cl::Device& device = devices[device_num]; + std::string device_version = device.getInfo(); + fprintf(stderr, "Using device: %s (%s)\n", device.getInfo().c_str(),device_version.c_str()); + + if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) + { + debugf("OpenCL 1.0 is not supported.\n"); + return false; + } + if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0) + { + m_opencl_1_1 = true; + } + + // create context + m_context = cl::Context(std::vector(&device, &device + 1)); + m_queue = cl::CommandQueue(m_context, device); + + // use requested workgroup size, but we require multiple of 8 + m_workgroup_size = ((workgroup_size + 7) / 8) * 8; + + // patch source code + std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE); + add_definition(code, "GROUP_SIZE", m_workgroup_size); + add_definition(code, "DAG_SIZE", (unsigned)(params.full_size / ETHASH_MIX_BYTES)); + add_definition(code, "ACCESSES", ETHASH_ACCESSES); + add_definition(code, "MAX_OUTPUTS", c_max_search_results); + //debugf("%s", code.c_str()); + + // create miner OpenCL program + cl::Program::Sources sources; + sources.push_back({code.c_str(), code.size()}); + + cl::Program program(m_context, sources); + try + { + program.build({device}); + } + catch (cl::Error err) + { + debugf("%s\n", program.getBuildInfo(device).c_str()); + return false; + } + m_hash_kernel = cl::Kernel(program, "ethash_hash"); + m_search_kernel = cl::Kernel(program, "ethash_search"); + + // create buffer for dag + m_dag = cl::Buffer(m_context, CL_MEM_READ_ONLY, params.full_size); + + // create buffer for header + m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32); + + // compute dag on CPU + { + // if this throws then it's because we probably need to subdivide the dag uploads for compatibility + void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, params.full_size); + // memcpying 1GB: horrible... really. horrible. but necessary since we can't mmap *and* gpumap. + _fillDAG(dag_ptr); + m_queue.enqueueUnmapMemObject(m_dag, dag_ptr); + } + + // create mining buffers + for (unsigned i = 0; i != c_num_buffers; ++i) + { + m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32*c_hash_batch_size); + m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t)); + } + return true; +} + +void ethash_cl_miner::hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count) +{ + struct pending_batch + { + unsigned base; + unsigned count; + unsigned buf; + }; + std::queue pending; + + // update header constant buffer + m_queue.enqueueWriteBuffer(m_header, true, 0, 32, header); + + /* + __kernel void ethash_combined_hash( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) + */ + m_hash_kernel.setArg(1, m_header); + m_hash_kernel.setArg(2, m_dag); + m_hash_kernel.setArg(3, nonce); + m_hash_kernel.setArg(4, ~0u); // have to pass this to stop the compile unrolling the loop + + unsigned buf = 0; + for (unsigned i = 0; i < count || !pending.empty(); ) + { + // how many this batch + if (i < count) + { + unsigned const this_count = std::min(count - i, c_hash_batch_size); + unsigned const batch_count = std::max(this_count, m_workgroup_size); + + // supply output hash buffer to kernel + m_hash_kernel.setArg(0, m_hash_buf[buf]); + + // execute it! + m_queue.enqueueNDRangeKernel( + m_hash_kernel, + cl::NullRange, + cl::NDRange(batch_count), + cl::NDRange(m_workgroup_size) + ); + m_queue.flush(); + + pending.push({i, this_count, buf}); + i += this_count; + buf = (buf + 1) % c_num_buffers; + } + + // read results + if (i == count || pending.size() == c_num_buffers) + { + pending_batch const& batch = pending.front(); + + // could use pinned host pointer instead, but this path isn't that important. + uint8_t* hashes = (uint8_t*)m_queue.enqueueMapBuffer(m_hash_buf[batch.buf], true, CL_MAP_READ, 0, batch.count * ETHASH_BYTES); + memcpy(ret + batch.base*ETHASH_BYTES, hashes, batch.count*ETHASH_BYTES); + m_queue.enqueueUnmapMemObject(m_hash_buf[batch.buf], hashes); + + pending.pop(); + } + } +} + + +void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook) +{ + struct pending_batch + { + uint64_t start_nonce; + unsigned buf; + }; + std::queue pending; + + static uint32_t const c_zero = 0; + + // update header constant buffer + m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); + for (unsigned i = 0; i != c_num_buffers; ++i) + { + m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero); + } + +#if CL_VERSION_1_2 && 0 + cl::Event pre_return_event; + if (!m_opencl_1_1) + { + m_queue.enqueueBarrierWithWaitList(NULL, &pre_return_event); + } + else +#endif + { + m_queue.finish(); + } + + /* + __kernel void ethash_combined_search( + __global hash32_t* g_hashes, // 0 + __constant hash32_t const* g_header, // 1 + __global hash128_t const* g_dag, // 2 + ulong start_nonce, // 3 + ulong target, // 4 + uint isolate // 5 + ) + */ + m_search_kernel.setArg(1, m_header); + m_search_kernel.setArg(2, m_dag); + + // pass these to stop the compiler unrolling the loops + m_search_kernel.setArg(4, target); + m_search_kernel.setArg(5, ~0u); + + + unsigned buf = 0; + for (uint64_t start_nonce = 0; ; start_nonce += c_search_batch_size) + { + // supply output buffer to kernel + m_search_kernel.setArg(0, m_search_buf[buf]); + m_search_kernel.setArg(3, start_nonce); + + // execute it! + m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size); + + pending.push({start_nonce, buf}); + buf = (buf + 1) % c_num_buffers; + + // read results + if (pending.size() == c_num_buffers) + { + pending_batch const& batch = pending.front(); + + // could use pinned host pointer instead + uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1+c_max_search_results) * sizeof(uint32_t)); + unsigned num_found = std::min(results[0], c_max_search_results); + + uint64_t nonces[c_max_search_results]; + for (unsigned i = 0; i != num_found; ++i) + { + nonces[i] = batch.start_nonce + results[i+1]; + } + + m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results); + + bool exit = num_found && hook.found(nonces, num_found); + exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit + if (exit) + break; + + // reset search buffer if we're still going + if (num_found) + m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero); + + pending.pop(); + } + } + + // not safe to return until this is ready +#if CL_VERSION_1_2 && 0 + if (!m_opencl_1_1) + { + pre_return_event.wait(); + } +#endif +} + diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h new file mode 100644 index 000000000..e478c739f --- /dev/null +++ b/libethash-cl/ethash_cl_miner.h @@ -0,0 +1,43 @@ +#pragma once + +#define __CL_ENABLE_EXCEPTIONS +#define CL_USE_DEPRECATED_OPENCL_2_0_APIS +#include "cl.hpp" +#include +#include +#include + +class ethash_cl_miner +{ +public: + struct search_hook + { + // reports progress, return true to abort + virtual bool found(uint64_t const* nonces, uint32_t count) = 0; + virtual bool searched(uint64_t start_nonce, uint32_t count) = 0; + }; + +public: + ethash_cl_miner(); + + bool init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size = 64); + + void finish(); + void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count); + void search(uint8_t const* header, uint64_t target, search_hook& hook); + +private: + enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 }; + + ethash_params m_params; + cl::Context m_context; + cl::CommandQueue m_queue; + cl::Kernel m_hash_kernel; + cl::Kernel m_search_kernel; + cl::Buffer m_dag; + cl::Buffer m_header; + cl::Buffer m_hash_buf[c_num_buffers]; + cl::Buffer m_search_buf[c_num_buffers]; + unsigned m_workgroup_size; + bool m_opencl_1_1; +}; diff --git a/libethash-cl/ethash_cl_miner_kernel.cl b/libethash-cl/ethash_cl_miner_kernel.cl new file mode 100644 index 000000000..3c8b9dc92 --- /dev/null +++ b/libethash-cl/ethash_cl_miner_kernel.cl @@ -0,0 +1,460 @@ +// author Tim Hughes +// Tested on Radeon HD 7850 +// Hashrate: 15940347 hashes/s +// Bandwidth: 124533 MB/s +// search kernel should fit in <= 84 VGPRS (3 wavefronts) + +#define THREADS_PER_HASH (128 / 16) +#define HASHES_PER_LOOP (GROUP_SIZE / THREADS_PER_HASH) + +#define FNV_PRIME 0x01000193 + +__constant uint2 const Keccak_f1600_RC[24] = { + (uint2)(0x00000001, 0x00000000), + (uint2)(0x00008082, 0x00000000), + (uint2)(0x0000808a, 0x80000000), + (uint2)(0x80008000, 0x80000000), + (uint2)(0x0000808b, 0x00000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008009, 0x80000000), + (uint2)(0x0000008a, 0x00000000), + (uint2)(0x00000088, 0x00000000), + (uint2)(0x80008009, 0x00000000), + (uint2)(0x8000000a, 0x00000000), + (uint2)(0x8000808b, 0x00000000), + (uint2)(0x0000008b, 0x80000000), + (uint2)(0x00008089, 0x80000000), + (uint2)(0x00008003, 0x80000000), + (uint2)(0x00008002, 0x80000000), + (uint2)(0x00000080, 0x80000000), + (uint2)(0x0000800a, 0x00000000), + (uint2)(0x8000000a, 0x80000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008080, 0x80000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008008, 0x80000000), +}; + +void keccak_f1600_round(uint2* a, uint r, uint out_size) +{ + #if !__ENDIAN_LITTLE__ + for (uint i = 0; i != 25; ++i) + a[i] = a[i].yx; + #endif + + uint2 b[25]; + uint2 t; + + // Theta + b[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; + b[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; + b[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; + b[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; + b[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; + t = b[4] ^ (uint2)(b[1].x << 1 | b[1].y >> 31, b[1].y << 1 | b[1].x >> 31); + a[0] ^= t; + a[5] ^= t; + a[10] ^= t; + a[15] ^= t; + a[20] ^= t; + t = b[0] ^ (uint2)(b[2].x << 1 | b[2].y >> 31, b[2].y << 1 | b[2].x >> 31); + a[1] ^= t; + a[6] ^= t; + a[11] ^= t; + a[16] ^= t; + a[21] ^= t; + t = b[1] ^ (uint2)(b[3].x << 1 | b[3].y >> 31, b[3].y << 1 | b[3].x >> 31); + a[2] ^= t; + a[7] ^= t; + a[12] ^= t; + a[17] ^= t; + a[22] ^= t; + t = b[2] ^ (uint2)(b[4].x << 1 | b[4].y >> 31, b[4].y << 1 | b[4].x >> 31); + a[3] ^= t; + a[8] ^= t; + a[13] ^= t; + a[18] ^= t; + a[23] ^= t; + t = b[3] ^ (uint2)(b[0].x << 1 | b[0].y >> 31, b[0].y << 1 | b[0].x >> 31); + a[4] ^= t; + a[9] ^= t; + a[14] ^= t; + a[19] ^= t; + a[24] ^= t; + + // Rho Pi + b[0] = a[0]; + b[10] = (uint2)(a[1].x << 1 | a[1].y >> 31, a[1].y << 1 | a[1].x >> 31); + b[7] = (uint2)(a[10].x << 3 | a[10].y >> 29, a[10].y << 3 | a[10].x >> 29); + b[11] = (uint2)(a[7].x << 6 | a[7].y >> 26, a[7].y << 6 | a[7].x >> 26); + b[17] = (uint2)(a[11].x << 10 | a[11].y >> 22, a[11].y << 10 | a[11].x >> 22); + b[18] = (uint2)(a[17].x << 15 | a[17].y >> 17, a[17].y << 15 | a[17].x >> 17); + b[3] = (uint2)(a[18].x << 21 | a[18].y >> 11, a[18].y << 21 | a[18].x >> 11); + b[5] = (uint2)(a[3].x << 28 | a[3].y >> 4, a[3].y << 28 | a[3].x >> 4); + b[16] = (uint2)(a[5].y << 4 | a[5].x >> 28, a[5].x << 4 | a[5].y >> 28); + b[8] = (uint2)(a[16].y << 13 | a[16].x >> 19, a[16].x << 13 | a[16].y >> 19); + b[21] = (uint2)(a[8].y << 23 | a[8].x >> 9, a[8].x << 23 | a[8].y >> 9); + b[24] = (uint2)(a[21].x << 2 | a[21].y >> 30, a[21].y << 2 | a[21].x >> 30); + b[4] = (uint2)(a[24].x << 14 | a[24].y >> 18, a[24].y << 14 | a[24].x >> 18); + b[15] = (uint2)(a[4].x << 27 | a[4].y >> 5, a[4].y << 27 | a[4].x >> 5); + b[23] = (uint2)(a[15].y << 9 | a[15].x >> 23, a[15].x << 9 | a[15].y >> 23); + b[19] = (uint2)(a[23].y << 24 | a[23].x >> 8, a[23].x << 24 | a[23].y >> 8); + b[13] = (uint2)(a[19].x << 8 | a[19].y >> 24, a[19].y << 8 | a[19].x >> 24); + b[12] = (uint2)(a[13].x << 25 | a[13].y >> 7, a[13].y << 25 | a[13].x >> 7); + b[2] = (uint2)(a[12].y << 11 | a[12].x >> 21, a[12].x << 11 | a[12].y >> 21); + b[20] = (uint2)(a[2].y << 30 | a[2].x >> 2, a[2].x << 30 | a[2].y >> 2); + b[14] = (uint2)(a[20].x << 18 | a[20].y >> 14, a[20].y << 18 | a[20].x >> 14); + b[22] = (uint2)(a[14].y << 7 | a[14].x >> 25, a[14].x << 7 | a[14].y >> 25); + b[9] = (uint2)(a[22].y << 29 | a[22].x >> 3, a[22].x << 29 | a[22].y >> 3); + b[6] = (uint2)(a[9].x << 20 | a[9].y >> 12, a[9].y << 20 | a[9].x >> 12); + b[1] = (uint2)(a[6].y << 12 | a[6].x >> 20, a[6].x << 12 | a[6].y >> 20); + + // Chi + a[0] = bitselect(b[0] ^ b[2], b[0], b[1]); + a[1] = bitselect(b[1] ^ b[3], b[1], b[2]); + a[2] = bitselect(b[2] ^ b[4], b[2], b[3]); + a[3] = bitselect(b[3] ^ b[0], b[3], b[4]); + if (out_size >= 4) + { + a[4] = bitselect(b[4] ^ b[1], b[4], b[0]); + a[5] = bitselect(b[5] ^ b[7], b[5], b[6]); + a[6] = bitselect(b[6] ^ b[8], b[6], b[7]); + a[7] = bitselect(b[7] ^ b[9], b[7], b[8]); + a[8] = bitselect(b[8] ^ b[5], b[8], b[9]); + if (out_size >= 8) + { + a[9] = bitselect(b[9] ^ b[6], b[9], b[5]); + a[10] = bitselect(b[10] ^ b[12], b[10], b[11]); + a[11] = bitselect(b[11] ^ b[13], b[11], b[12]); + a[12] = bitselect(b[12] ^ b[14], b[12], b[13]); + a[13] = bitselect(b[13] ^ b[10], b[13], b[14]); + a[14] = bitselect(b[14] ^ b[11], b[14], b[10]); + a[15] = bitselect(b[15] ^ b[17], b[15], b[16]); + a[16] = bitselect(b[16] ^ b[18], b[16], b[17]); + a[17] = bitselect(b[17] ^ b[19], b[17], b[18]); + a[18] = bitselect(b[18] ^ b[15], b[18], b[19]); + a[19] = bitselect(b[19] ^ b[16], b[19], b[15]); + a[20] = bitselect(b[20] ^ b[22], b[20], b[21]); + a[21] = bitselect(b[21] ^ b[23], b[21], b[22]); + a[22] = bitselect(b[22] ^ b[24], b[22], b[23]); + a[23] = bitselect(b[23] ^ b[20], b[23], b[24]); + a[24] = bitselect(b[24] ^ b[21], b[24], b[20]); + } + } + + // Iota + a[0] ^= Keccak_f1600_RC[r]; + + #if !__ENDIAN_LITTLE__ + for (uint i = 0; i != 25; ++i) + a[i] = a[i].yx; + #endif +} + +void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) +{ + for (uint i = in_size; i != 25; ++i) + { + a[i] = 0; + } +#if __ENDIAN_LITTLE__ + a[in_size] ^= 0x0000000000000001; + a[24-out_size*2] ^= 0x8000000000000000; +#else + a[in_size] ^= 0x0100000000000000; + a[24-out_size*2] ^= 0x0000000000000080; +#endif + + // Originally I unrolled the first and last rounds to interface + // better with surrounding code, however I haven't done this + // without causing the AMD compiler to blow up the VGPR usage. + uint r = 0; + do + { + // This dynamic branch stops the AMD compiler unrolling the loop + // and additionally saves about 33% of the VGPRs, enough to gain another + // wavefront. Ideally we'd get 4 in flight, but 3 is the best I can + // massage out of the compiler. It doesn't really seem to matter how + // much we try and help the compiler save VGPRs because it seems to throw + // that information away, hence the implementation of keccak here + // doesn't bother. + if (isolate) + { + keccak_f1600_round((uint2*)a, r++, 25); + } + } + while (r < 23); + + // final round optimised for digest size + keccak_f1600_round((uint2*)a, r++, out_size); +} + +#define copy(dst, src, count) for (uint i = 0; i != count; ++i) { (dst)[i] = (src)[i]; } + +#define countof(x) (sizeof(x) / sizeof(x[0])) + +uint fnv(uint x, uint y) +{ + return x * FNV_PRIME ^ y; +} + +uint4 fnv4(uint4 x, uint4 y) +{ + return x * FNV_PRIME ^ y; +} + +uint fnv_reduce(uint4 v) +{ + return fnv(fnv(fnv(v.x, v.y), v.z), v.w); +} + +typedef union +{ + ulong ulongs[32 / sizeof(ulong)]; + uint uints[32 / sizeof(uint)]; +} hash32_t; + +typedef union +{ + ulong ulongs[64 / sizeof(ulong)]; + uint4 uint4s[64 / sizeof(uint4)]; +} hash64_t; + +typedef union +{ + uint uints[128 / sizeof(uint)]; + uint4 uint4s[128 / sizeof(uint4)]; +} hash128_t; + +hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) +{ + hash64_t init; + uint const init_size = countof(init.ulongs); + uint const hash_size = countof(header->ulongs); + + // sha3_512(header .. nonce) + ulong state[25]; + copy(state, header->ulongs, hash_size); + state[hash_size] = nonce; + keccak_f1600_no_absorb(state, hash_size + 1, init_size, isolate); + + copy(init.ulongs, state, init_size); + return init; +} + +uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) +{ + uint4 mix = init; + + // share init0 + if (thread_id == 0) + *share = mix.x; + barrier(CLK_LOCAL_MEM_FENCE); + uint init0 = *share; + + uint a = 0; + do + { + bool update_share = thread_id == (a/4) % THREADS_PER_HASH; + + #pragma unroll + for (uint i = 0; i != 4; ++i) + { + if (update_share) + { + uint m[4] = { mix.x, mix.y, mix.z, mix.w }; + *share = fnv(init0 ^ (a+i), m[i]) % DAG_SIZE; + } + barrier(CLK_LOCAL_MEM_FENCE); + + mix = fnv4(mix, g_dag[*share].uint4s[thread_id]); + } + } + while ((a += 4) != (ACCESSES & isolate)); + + return fnv_reduce(mix); +} + +hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) +{ + ulong state[25]; + + hash32_t hash; + uint const hash_size = countof(hash.ulongs); + uint const init_size = countof(init->ulongs); + uint const mix_size = countof(mix->ulongs); + + // keccak_256(keccak_512(header..nonce) .. mix); + copy(state, init->ulongs, init_size); + copy(state + init_size, mix->ulongs, mix_size); + keccak_f1600_no_absorb(state, init_size+mix_size, hash_size, isolate); + + // copy out + copy(hash.ulongs, state, hash_size); + return hash; +} + +hash32_t compute_hash_simple( + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong nonce, + uint isolate + ) +{ + hash64_t init = init_hash(g_header, nonce, isolate); + + hash128_t mix; + for (uint i = 0; i != countof(mix.uint4s); ++i) + { + mix.uint4s[i] = init.uint4s[i % countof(init.uint4s)]; + } + + uint mix_val = mix.uints[0]; + uint init0 = mix.uints[0]; + uint a = 0; + do + { + uint pi = fnv(init0 ^ a, mix_val) % DAG_SIZE; + uint n = (a+1) % countof(mix.uints); + + #pragma unroll + for (uint i = 0; i != countof(mix.uints); ++i) + { + mix.uints[i] = fnv(mix.uints[i], g_dag[pi].uints[i]); + mix_val = i == n ? mix.uints[i] : mix_val; + } + } + while (++a != (ACCESSES & isolate)); + + // reduce to output + hash32_t fnv_mix; + for (uint i = 0; i != countof(fnv_mix.uints); ++i) + { + fnv_mix.uints[i] = fnv_reduce(mix.uint4s[i]); + } + + return final_hash(&init, &fnv_mix, isolate); +} + +typedef union +{ + struct + { + hash64_t init; + uint pad; // avoid lds bank conflicts + }; + hash32_t mix; +} compute_hash_share; + +hash32_t compute_hash( + __local compute_hash_share* share, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + + // Compute one init hash per work item. + hash64_t init = init_hash(g_header, nonce, isolate); + + // Threads work together in this phase in groups of 8. + uint const thread_id = gid % THREADS_PER_HASH; + uint const hash_id = (gid % GROUP_SIZE) / THREADS_PER_HASH; + + hash32_t mix; + uint i = 0; + do + { + // share init with other threads + if (i == thread_id) + share[hash_id].init = init; + barrier(CLK_LOCAL_MEM_FENCE); + + uint4 thread_init = share[hash_id].init.uint4s[thread_id % (64 / sizeof(uint4))]; + barrier(CLK_LOCAL_MEM_FENCE); + + uint thread_mix = inner_loop(thread_init, thread_id, share[hash_id].mix.uints, g_dag, isolate); + + share[hash_id].mix.uints[thread_id] = thread_mix; + barrier(CLK_LOCAL_MEM_FENCE); + + if (i == thread_id) + mix = share[hash_id].mix; + barrier(CLK_LOCAL_MEM_FENCE); + } + while (++i != (THREADS_PER_HASH & isolate)); + + return final_hash(&init, &mix, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash_simple( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search_simple( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + uint const gid = get_global_id(0); + hash32_t hash = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); + if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target) + { + uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1); + g_output[slot] = gid; + } +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + hash32_t hash = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate); + + if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target) + { + uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1); + g_output[slot] = gid; + } +} From e7c81896675ab68b514896530fe3ec5f3c62714d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 12:14:35 +0200 Subject: [PATCH 03/34] Fixes #1597 --- libethereum/State.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 6e94a3406..e77565837 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -451,7 +451,7 @@ bool State::cull(TransactionQueue& _tq) const { try { - if (i.second.nonce() <= transactionsFrom(i.second.sender())) + if (i.second.nonce() < transactionsFrom(i.second.sender())) { _tq.drop(i.first); ret = true; From 87bb1195a5b1caf1386b12471d5853a086c09c1d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 12:15:17 +0200 Subject: [PATCH 04/34] Half-finished Miner/Farm framework. --- libdevcore/Worker.cpp | 16 ++-- libdevcore/Worker.h | 6 +- libethcore/Miner.cpp | 12 +++ libethcore/Miner.h | 146 +++++++++++++++++++++++++++++ libethcore/ProofOfWork.cpp | 56 ++++++----- libethcore/ProofOfWork.h | 126 ++++++++----------------- libethereum/Client.cpp | 2 +- libethereum/Client.h | 4 +- libethereum/Farm.cpp | 12 +++ libethereum/Farm.h | 153 +++++++++++++++++++++++++++++++ libethereum/Miner.cpp | 2 +- libethereum/Miner.h | 97 +------------------- libethereum/TransactionQueue.cpp | 5 +- libethereum/TransactionQueue.h | 14 ++- libethereumx/Ethereum.h | 2 +- 15 files changed, 432 insertions(+), 221 deletions(-) create mode 100644 libethcore/Miner.cpp create mode 100644 libethcore/Miner.h create mode 100644 libethereum/Farm.cpp create mode 100644 libethereum/Farm.h diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index 175323620..bc8fe97f2 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -39,12 +39,7 @@ void Worker::startWorking() { setThreadName(m_name.c_str()); startedWorking(); - while (!m_stop) - { - if (m_idleWaitMs) - this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs)); - doWork(); - } + workLoop(); cnote << "Finishing up worker thread"; doneWorking(); })); @@ -63,3 +58,12 @@ void Worker::stopWorking() cnote << "Stopped" << m_name; } +void Worker::workLoop() +{ + while (!m_stop) + { + if (m_idleWaitMs) + this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs)); + doWork(); + } +} diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 40bc118aa..6a35d6c4c 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -57,7 +57,11 @@ protected: virtual void startedWorking() {} /// Called continuously following sleep for m_idleWaitMs. - virtual void doWork() = 0; + virtual void doWork() {} + + /// Overrides doWork(); should call shouldStop() often and exit when true. + virtual void workLoop(); + bool shouldStop() const { return m_stop; } /// Called when is to be stopped, just prior to thread being joined. virtual void doneWorking() {} diff --git a/libethcore/Miner.cpp b/libethcore/Miner.cpp new file mode 100644 index 000000000..d6f15866f --- /dev/null +++ b/libethcore/Miner.cpp @@ -0,0 +1,12 @@ +#include "Miner.h" + +Miner::Miner() +{ + +} + +Miner::~Miner() +{ + +} + diff --git a/libethcore/Miner.h b/libethcore/Miner.h new file mode 100644 index 000000000..a7f17e565 --- /dev/null +++ b/libethcore/Miner.h @@ -0,0 +1,146 @@ +/* + 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 Miner.h + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "State.h" + +namespace dev +{ + +namespace eth +{ + +struct WorkPackage +{ + h256 boundary; + h256 headerHash; ///< When h256() means "pause until notified a new work package is available". + h256 seedHash; +}; + +static const WorkPackage NullWorkPackage; + +/** + * @brief Describes the progress of a mining operation. + */ +struct MiningProgress +{ + void combine(MiningProgress 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. + unsigned hashes = 0; ///< Total number of hashes computed. + unsigned ms = 0; ///< Total number of milliseconds of mining thus far. +}; + +/** + * @brief Class for hosting one or more Miners. + * @warning Must be implemented in a threadsafe manner since it will be called from multiple + * miner threads. + */ +class FarmFace +{ +public: + /** + * @brief Called from a Miner to note a WorkPackage has a solution. + * @param _p The solution. + * @param _wp The WorkPackage that the Solution is for. + * @return true iff the solution was good (implying that mining should be . + */ + virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; +}; + +/** + * @brief A miner - a member and adoptee of the Farm. + */ +class Miner +{ +public: + using ConstructionInfo = std::pair; + + Miner(ConstructionInfo const& _ci): + m_farm(_ci.first), + m_index(_ci.second) + {} + + // API FOR THE FARM TO CALL IN WITH + + void setWork(WorkPackage const& _work = WorkPackage()) + { + Guard l(x_work); + if (_work.headerHash != h256()) + kickOff(m_work); + else if (m_work.headerHash == h256() && _work.headerHash != h256()) + pause(); + m_work = _work; + } + + unsigned index() const { return m_index; } + +protected: + + // REQUIRED TO BE REIMPLEMENTED BY A SUBCLASS: + + /** + * @brief Begin working on a given work package, discarding any previous work. + * @param _work The package for which to find a solution. + */ + virtual void kickOff(WorkPackage const& _work) = 0; + + /** + * @brief No work left to be done. Pause until told to kickOff(). + */ + virtual void pause() = 0; + + // AVAILABLE FOR A SUBCLASS TO CALL: + + /** + * @brief Notes that the Miner found a solution. + * @param _s The solution. + * @return true if the solution was correct and that the miner should pause. + */ + bool submitProof(ProofOfWork::Solution const& _s) + { + if (m_farm) + { + Guard l(x_work); + return m_farm->submitProof(_s, m_work, this); + } + return true; + } + +private: + FarmFace* m_farm = nullptr; + unsigned m_index; + + Mutex x_work; + WorkPackage m_work; +}; + +} +} diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index ffa787e3e..1b3b55f4d 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -45,13 +45,38 @@ namespace dev namespace eth { -bool EthashPoW::verify(BlockInfo const& _header) +void Ethash::CPUMiner::workLoop() { - return Ethasher::verify(_header); -} + Solution solution; + + class Miner + { + public: + Miner(BlockInfo const& _header): + m_headerHash(_header.headerHash(WithoutNonce)), + m_params(Ethasher::params(_header)), + m_datasetPointer(Ethasher::get()->full(_header).data()) + {} + + inline h256 mine(uint64_t _nonce) + { + ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); +// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer); + return h256(m_ethashReturn.result, h256::ConstructFromPointer); + } + + inline h256 lastMixHash() const + { + return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); + } + + private: + ethash_return_value m_ethashReturn; + h256 m_headerHash; + ethash_params m_params; + void const* m_datasetPointer; + }; -std::pair EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) -{ Ethasher::Miner m(_header); std::pair ret; @@ -70,34 +95,21 @@ std::pair EthashCPU::mine(BlockInfo const& _heade double best = 1e99; // high enough to be effectively infinity :) Solution result; unsigned hashCount = 0; - for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++) + for (; !shouldStop(); tryNonce++, hashCount++) { h256 val(m.mine(tryNonce)); best = std::min(best, log2((double)(u256)val)); if (val <= boundary) { - ret.first.completed = true; - assert(Ethasher::eval(_header, (Nonce)(u64)tryNonce).value == val); - result.mixHash = m.lastMixHash(); - result.nonce = u64(tryNonce); - BlockInfo test = _header; - assignResult(result, test); - assert(verify(test)); - break; + if (submitProof(solution)) + return; } } ret.first.hashes = hashCount; ret.first.best = best; ret.second = result; - if (ret.first.completed) - { - BlockInfo test = _header; - assignResult(result, test); - assert(verify(test)); - } - - return ret; + return; } #if ETH_ETHASHCL || !ETH_TRUE diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index bd8ab58db..3ea177a9a 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -29,6 +29,7 @@ #include #include "Common.h" #include "BlockInfo.h" +#include "Miner.h" #define FAKE_DAGGER 1 @@ -51,39 +52,56 @@ struct MineInfo bool completed = false; }; -class EthashPoW +class EthashCLHook; + +class Ethash { -public: - struct Solution - { - Nonce nonce; - h256 mixHash; - }; - static bool verify(BlockInfo const& _header); - static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } +public: - virtual unsigned defaultTimeout() const { return 100; } - virtual std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0; +struct Solution +{ + Nonce nonce; + h256 mixHash; }; -class EthashCPU: public EthashPoW +static bool verify(BlockInfo const& _header); +static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + +class CPUMiner: public Miner, Worker { public: - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; + CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} -protected: - Nonce m_last; + static unsigned instances() { return thread::hardware_concurrency(); } + + void kickOff(WorkPackage const& _work) override + { + stopWorking(); + m_work = _work; + startWorking(); + } + + void pause() override { stopWorking(); } + +private: + void workLoop() override; + + WorkPackage m_work; + MineInfo m_info; }; #if ETH_ETHASHCL || !ETH_TRUE -class EthashCLHook; -class EthashCL: public EthashPoW +class GPUMiner: public NewMiner { public: - EthashCL(); - ~EthashCL(); + GPUMiner(ConstructionInfo const& _ci): NewMiner(_ci) + { + + } + + static unsigned instances() { return 1; } std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; unsigned defaultTimeout() const override { return 500; } @@ -96,81 +114,15 @@ protected: std::unique_ptr m_hook; }; -using Ethash = EthashCL; #else -using Ethash = EthashCPU; -#endif -template -class ProofOfWorkEngine: public Evaluator -{ -public: - using Solution = Nonce; +using GPUMiner = CPUMiner; - static bool verify(BlockInfo const& _header) { return (bigint)(u256)Evaluator::eval(_header.headerHash(WithoutNonce), _header.nonce) <= (bigint(1) << 256) / _header.difficulty; } - inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true); - static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r; } - unsigned defaultTimeout() const { return 100; } - -protected: - Nonce m_last; -}; +#endif -class SHA3Evaluator -{ -public: - static h256 eval(h256 const& _root, Nonce const& _nonce) { h256 b[2] = { _root, h256(_nonce) }; return sha3(bytesConstRef((byte const*)&b[0], 64)); } }; -using SHA3ProofOfWork = ProofOfWorkEngine; - using ProofOfWork = Ethash; -template -std::pair::Solution> ProofOfWorkEngine::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) -{ - auto headerHashWithoutNonce = _header.headerHash(WithoutNonce); - auto difficulty = _header.difficulty; - - std::pair ret; - static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()))); - Nonce::Arith s = (m_last = Nonce::random(s_eng)); - - bigint d = (bigint(1) << 256) / difficulty; - ret.first.requirement = log2((double)d); - - // 2^ 0 32 64 128 256 - // [--------*-------------------------] - // - // evaluate until we run out of time - auto startTime = std::chrono::steady_clock::now(); - double best = 1e99; // high enough to be effectively infinity :) - ProofOfWorkEngine::Solution solution; - unsigned h = 0; - for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++) - { - solution = (ProofOfWorkEngine::Solution)s; - auto e = (bigint)(u256)Evaluator::eval(headerHashWithoutNonce, solution); - best = std::min(best, log2((double)e)); - if (e <= d) - { - ret.first.completed = true; - break; - } - } - ret.first.hashes = h; - ret.first.best = best; - ret.second = solution; - - if (ret.first.completed) - { - BlockInfo test = _header; - assignResult(solution, test); - assert(verify(test)); - } - - return ret; -} - } } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 223384c3a..278c02fa8 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -467,7 +467,7 @@ void Client::doWork() cworkin << "WORK"; h256Set changeds; - auto maintainMiner = [&](Miner& m) + auto maintainMiner = [&](OldMiner& m) { if (m.isComplete()) { diff --git a/libethereum/Client.h b/libethereum/Client.h index ec852afd2..9af501f74 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -72,7 +72,7 @@ private: std::string m_path; }; -class RemoteMiner: public Miner +class RemoteMiner: public OldMiner { public: RemoteMiner() {} @@ -124,7 +124,7 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C */ class Client: public MinerHost, public ClientBase, Worker { - friend class Miner; + friend class OldMiner; public: /// New-style Constructor. diff --git a/libethereum/Farm.cpp b/libethereum/Farm.cpp new file mode 100644 index 000000000..639e4efcf --- /dev/null +++ b/libethereum/Farm.cpp @@ -0,0 +1,12 @@ +#include "Farm.h" + +Farm::Farm() +{ + +} + +Farm::~Farm() +{ + +} + diff --git a/libethereum/Farm.h b/libethereum/Farm.h new file mode 100644 index 000000000..a49038f0d --- /dev/null +++ b/libethereum/Farm.h @@ -0,0 +1,153 @@ +/* + 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 Miner.h + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "Miner.h" + +namespace dev +{ + +namespace eth +{ + +/** + * @brief A collective of Miners. + * Miners ask for work, then submit proofs + * @threadsafe + */ +template +class Farm: public FarmFace +{ +public: + /** + * @brief Sets the current mining mission. + * @param _bi The block (header) we wish to be mining. + */ + void setWork(BlockInfo const& _bi) + { + WriteGuard l(x_work); + m_header = _bi; + m_work.boundary = _bi.boundary(); + m_work.headerHash = _bi.headerHash(WithNonce); + m_work.seedHash = _bi.seedHash(); + ReadGuard l(x_miners); + for (auto const& m: miners) + m->setWork(m_work); + } + + /** + * @brief (Re)start miners for CPU only. + * @returns true if started properly. + */ + bool startCPU() { return start(); } + + /** + * @brief (Re)start miners for GPU only. + * @returns true if started properly. + */ + bool startGPU() { start(); } + + /** + * @brief Stop all mining activities. + */ + void stop() + { + WriteGuard l(x_miners); + m_miners.clear(); + } + + /** + * @brief Get information on the progress of mining this work package. + * @return The progress with mining so far. + */ + MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } + +protected: + // TO BE REIMPLEMENTED BY THE SUBCLASS + /** + * @brief Provides a valid header based upon that received previously with setWork(). + * @param _bi The now-valid header. + * @return true if the header was good and that the Farm should pause until more work is submitted. + */ + virtual bool submitHeader(BlockInfo const& _bi) = 0; + +private: + /** + * @brief Called from a Miner to note a WorkPackage has a solution. + * @param _p The solution. + * @param _wp The WorkPackage that the Solution is for. + * @return true iff the solution was good (implying that mining should be . + */ + bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp, NewMiner* _m) override + { + if (_wp.headerHash != m_work.headerHash) + return false; + + ProofOfWork::assignResult(_p, m_header); + if (submitHeader(m_header)) + { + ReadGuard l(x_miners); + for (std::shared_ptr const& m: m_miners) + if (m.get() != _m) + m->pause(); + m_work.headerHash = h256(); + return true; + } + return false; + } + + /** + * @brief Start a number of miners. + */ + template + bool start() + { + WriteGuard l(x_miners); + if (!m_miners.empty() && !!std::dynamic_pointer_cast(m_miners[0])) + return true; + m_miners.clear(); + m_miners.reserve(MinerType::instances()); + for (unsigned i = 0; i < MinerType::instances(); ++i) + m_miners.push_back(new MinerType(std::make_pair(this, i))); + return true; + } + + mutable SharedMutex x_miners; + std::vector> m_miners; + + mutable SharedMutex x_progress; + MineProgress m_progress; + + mutable SharedMutex x_work; + WorkPackage m_work; + BlockInfo m_header; +}; + +} +} diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index dc3d9bd9e..b386fe868 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -Miner::~Miner() {} +OldMiner::~OldMiner() {} LocalMiner::LocalMiner(MinerHost* _host, unsigned _id) { diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 86d103db5..3abf93770 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "State.h" namespace dev @@ -36,28 +37,6 @@ namespace dev namespace eth { -struct WorkPackage -{ - h256 boundary; - h256 headerHash; ///< When h256() means "pause until notified a new work package is available". - h256 seedHash; -}; - -static const WorkPackage NullWorkPackage; - -/** - * @brief Describes the progress of a mining operation. - */ -struct MineProgress -{ - 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. - unsigned hashes = 0; ///< Total number of hashes computed. - unsigned ms = 0; ///< Total number of milliseconds of mining thus far. -}; - /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple @@ -66,10 +45,6 @@ struct MineProgress class MinerHost { public: - // ============================= NEW API ============================= - virtual WorkPackage const& getWork() const { return NullWorkPackage; } - - // ============================= OLD API ============================= virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. virtual void onProgressed() {} ///< Called once some progress has been made. virtual void onComplete() {} ///< Called once a block is found. @@ -77,17 +52,17 @@ public: virtual bool turbo() const = 0; ///< @returns true iff the Miner should use GPU if possible. }; -class Miner +class OldMiner { public: - virtual ~Miner(); + virtual ~OldMiner(); virtual void noteStateChange() = 0; virtual bool isComplete() const = 0; virtual bytes const& blockData() const = 0; }; -class AsyncMiner: public Miner +class AsyncMiner: public OldMiner { public: /// Null constructor. @@ -187,69 +162,5 @@ private: std::list m_mineHistory; ///< What the history of our mining? }; -/** - * @brief A collective of Miners. - * Miners ask for work, then submit proofs - * @threadsafe - */ -class Farm: public MinerHost -{ -public: - /** - * @brief Sets the current mining mission. - * @param _bi The block (header) we wish to be mining. - */ - void setWork(BlockInfo const& _bi); - - /** - * @brief (Re)start miners for CPU only. - * @returns true if started properly. - */ - bool startCPU(); - - /** - * @brief (Re)start miners for GPU only. - * @returns true if started properly. - */ - bool startGPU(); - - /** - * @brief Stop all mining activities. - */ - void stop(); - - /** - * @brief Get information on the progress of mining this work package. - * @return The progress with mining so far. - */ - MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } - -protected: - /** - * @brief Called by a Miner to retrieve a work package. Reimplemented from MinerHost. - * @return The work package to solve. - */ - virtual WorkPackage const& getWork() const override { ReadGuard l(x_work); return m_work; } - - /** - * @brief Called from a Miner to note a WorkPackage has a solution. - * @param _p The solution. - * @param _wp The WorkPackage that the Solution is for. - * @return true iff the solution was good (implying that mining should be . - */ - virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; - -private: - mutable SharedMutex x_miners; - std::vector> m_miners; - - mutable SharedMutex x_progress; - MineProgress m_progress; - - mutable SharedMutex x_work; - WorkPackage m_work; -}; - - } } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 38f3b9429..7c72f53e8 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) +ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb) { // Check if we already know this transaction. h256 h = sha3(_transactionRLP); @@ -50,7 +50,8 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) // If valid, append to blocks. m_current[h] = t; m_known.insert(h); - + if (_cb) + m_callbacks[h] = _cb; ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); } catch (Exception const& _e) diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 73ce24fbd..ad093b4e5 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -45,8 +46,10 @@ struct TransactionQueueChannel: public LogChannel { static const char* name() { class TransactionQueue { public: - ImportResult import(bytes const& _tx) { return import(&_tx); } - ImportResult import(bytesConstRef _tx); + using ImportCallback = std::function; + + ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx); } + ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback()); void drop(h256 _txHash); @@ -59,10 +62,11 @@ public: void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } private: - mutable boost::shared_mutex m_lock; ///< General lock. - std::set m_known; ///< Hashes of transactions in both sets. - std::map m_current; ///< Map of SHA3(tx) to tx. + mutable boost::shared_mutex m_lock; ///< General lock. + std::set m_known; ///< Hashes of transactions in both sets. + std::map m_current; ///< Map of SHA3(tx) to tx. std::multimap> m_unknown; ///< 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_callbacks; ///< Called once }; } diff --git a/libethereumx/Ethereum.h b/libethereumx/Ethereum.h index 7ff685339..15f00f4ae 100644 --- a/libethereumx/Ethereum.h +++ b/libethereumx/Ethereum.h @@ -52,7 +52,7 @@ class Client; */ class Ethereum { - friend class Miner; + friend class OldMiner; public: /// Constructor. After this, everything should be set up to go. From cc82d9fbc5db0484af98c2f4d1995a14a314f655 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 15:26:37 +0200 Subject: [PATCH 05/34] GPU Miner prototyped in new API. --- libethcore/Ethasher.cpp | 134 +++++++++++++++++++------------------ libethcore/Ethasher.h | 10 ++- libethcore/ProofOfWork.cpp | 88 ++++++++++++------------ libethcore/ProofOfWork.h | 32 ++++----- 4 files changed, 135 insertions(+), 129 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 6cd2504b3..2118952eb 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -39,6 +39,8 @@ using namespace chrono; using namespace dev; using namespace eth; +#define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} + Ethasher* dev::eth::Ethasher::s_this = nullptr; Ethasher::~Ethasher() @@ -59,28 +61,35 @@ void Ethasher::killCache(h256 const& _s) void const* Ethasher::light(BlockInfo const& _header) { - RecursiveGuard l(x_this); - if (_header.number > c_ethashEpochLength * 2048) - { - std::ostringstream error; - error << "block number is too high; max is " << c_ethashEpochLength * 2048 << "(was " << _header.number << ")"; - throw std::invalid_argument( error.str() ); - } + return light(_header.seedHash()); +} +void const* Ethasher::light(h256 const& _seedHash) +{ + RecursiveGuard l(x_this); if (!m_lights.count(_header.seedHash())) { - ethash_params p = params((unsigned)_header.number); - m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); + ethash_params p = params(_seedHash); + m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); } - return m_lights[_header.seedHash()]; + return m_lights[_seedHash]; } -#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} +bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest) +{ + return full(_header.seedHash(), _dest); +} -bytesConstRef Ethasher::full(BlockInfo const& _header) +bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) { RecursiveGuard l(x_this); - if (!m_fulls.count(_header.seedHash())) + if (m_fulls.count(_seedHash) && _dest) + { + assert(m_fulls.size() <= _dest.size()); + m_fulls.at(_seedHash).copyTo(_dest); + return; + } + if (!m_fulls.count(_seedHash)) { // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. /* if (!m_fulls.empty()) @@ -93,9 +102,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(c_ethashRevision, _header.seedHash()); + auto info = rlpList(c_ethashRevision, _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8)); + std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) { // memofile valid - rename. @@ -105,17 +114,26 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); - m_fulls[_header.seedHash()] = contentsNew(memoFile); - if (!m_fulls[_header.seedHash()]) + ethash_params p = params(_seedHash); + assert(!_dest || _dest.size() >= p.full_size); // must be big enough. + + bytesRef r = contentsNew(memoFile, _dest); + if (!r) { - ethash_params p = params((unsigned)_header.number); - m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); - auto c = light(_header); - ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c); - writeFile(memoFile, m_fulls[_header.seedHash()]); + // file didn't exist. + if (_dest) + // buffer was passed in - no insertion into cache nor need to allocate + r = _dest; + else + r = bytesRef(new byte[p.full_size], p.full_size); + ethash_prep_full(r, &p, light(_seedHash)); + writeFile(memoFile, r); } + if (_dest) + return _dest; + m_fulls[_seedHash] = r; } - return m_fulls[_header.seedHash()]; + return m_fulls[_seedHash]; } ethash_params Ethasher::params(BlockInfo const& _header) @@ -123,44 +141,6 @@ ethash_params Ethasher::params(BlockInfo const& _header) return params((unsigned)_header.number); } -void Ethasher::readFull(BlockInfo const& _header, void* _dest) -{ - if (!m_fulls.count(_header.seedHash())) - { - // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. -/* if (!m_fulls.empty()) - { - delete [] m_fulls.begin()->second.data(); - m_fulls.erase(m_fulls.begin()); - }*/ - - try { - boost::filesystem::create_directories(getDataDir("ethash")); - } catch (...) {} - - auto info = rlpList(c_ethashRevision, _header.seedHash()); - std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8)); - if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) - { - // memofile valid - rename. - boost::filesystem::rename(oldMemoFile, memoFile); - } - - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); - - ethash_params p = params((unsigned)_header.number); - bytesRef r = contentsNew(memoFile, bytesRef((byte*)_dest, p.full_size)); - if (!r) - { - auto c = light(_header); - ethash_prep_full(_dest, &p, c); - writeFile(memoFile, bytesConstRef((byte*)_dest, p.full_size)); - } - } -} - ethash_params Ethasher::params(unsigned _n) { ethash_params p; @@ -169,6 +149,27 @@ ethash_params Ethasher::params(unsigned _n) return p; } +ethash_params Ethasher::params(h256 const& _seedHash) +{ + unsigned epoch = 0; + try + { + epoch = m_seedHashes.at(_seedHash); + } + catch (...) + { + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} + if (epoch == 2048) + { + std::ostringstream error; + error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (c_ethashEpochLength * 2048); + throw std::invalid_argument(error.str()); + } + m_seedHashes[_seedHash] = epoch; + } + return params(epoch * c_ethashEpochLength); +} + bool Ethasher::verify(BlockInfo const& _header) { if (_header.number >= c_ethashEpochLength * 2048) @@ -208,13 +209,18 @@ bool Ethasher::verify(BlockInfo const& _header) } Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) +{ + return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); +} + +Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; - if (Ethasher::get()->m_fulls.count(_header.seedHash())) - ethash_compute_full(&r, Ethasher::get()->full(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + if (Ethasher::get()->m_fulls.count(_seedHash)) + ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else - ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + ethash_compute_light(&r, Ethasher::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h index 32622929f..f57c2f4f6 100644 --- a/libethcore/Ethasher.h +++ b/libethcore/Ethasher.h @@ -52,12 +52,14 @@ public: using FullType = void const*; LightType light(BlockInfo const& _header); - bytesConstRef full(BlockInfo const& _header); + LightType light(h256 const& _header); + bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); + bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); + static ethash_params params(BlockInfo const& _header); + static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); - void readFull(BlockInfo const& _header, void* _dest); - struct Result { h256 value; @@ -66,6 +68,7 @@ public: static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } static Result eval(BlockInfo const& _header, Nonce const& _nonce); + static Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); static bool verify(BlockInfo const& _header); class Miner @@ -103,6 +106,7 @@ private: RecursiveMutex x_this; std::map m_lights; std::map m_fulls; + std::map m_seedHashes; }; } diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index 1b3b55f4d..97488ee35 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -31,7 +31,7 @@ #include #include #include -#if ETH_ETHASHCL +#if ETH_ETHASHCL || !ETH_TRUE #include #endif #include "BlockInfo.h" @@ -134,10 +134,15 @@ public: }; */ -struct EthashCLHook: public ethash_cl_miner::search_hook +namespace dev { namespace eth { +class EthashCLHook: public ethash_cl_miner::search_hook { +public: + EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} + void abort() { + Guard l(x_all); if (m_aborted) return; // cdebug << "Attempting to abort"; @@ -147,21 +152,23 @@ struct EthashCLHook: public ethash_cl_miner::search_hook if (!m_aborted) cwarn << "Couldn't abort. Abandoning OpenCL process."; m_aborted = m_abort = false; - m_found.clear(); } - vector fetchFound() { vector ret; Guard l(x_all); std::swap(ret, m_found); return ret; } uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { - Guard l(x_all); - for (unsigned i = 0; i < _count; ++i) - m_found.push_back((Nonce)(u64)_nonces[i]); - m_aborted = true; // cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); - return true; + for (uint32_t i = 0; i < _count; ++i) + { + if (m_owner->found(_nonces[i])) + { + m_aborted = true; + return true; + } + } + return false; } virtual bool searched(uint64_t _startNonce, uint32_t _count) override @@ -180,66 +187,53 @@ protected: private: Mutex x_all; - vector m_found; uint64_t m_total; uint64_t m_last; bool m_abort = false; bool m_aborted = true; + Ethash::GPUMiner* m_owner = nullptr; }; -EthashCL::EthashCL(): - m_hook(new EthashCLHook) +} } + +Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): + Miner(_ci), + m_hook(new EthashCLHook(this)) { } -EthashCL::~EthashCL() +void Ethash::GPUMiner::report(uint64_t _nonce) { + Nonce n = (Nonce)(u64)_nonce; + Ethasher::Result r = Ethasher::eval(m_work.seedHash, m_work.headerHash, n); + if (r.value < m_work.boundary) + return submitProof(Solution{n, r.mixHash}); + return false; } -std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) +void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { - if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash()) + if (!m_miner || m_minerSeed != _work.seedHash) { if (m_miner) m_hook->abort(); m_miner.reset(new ethash_cl_miner); - auto cb = [&](void* d) { - Ethasher::get()->readFull(_header, d); - }; - m_miner->init(Ethasher::params(_header), cb, 32); + auto p = Ethasher::params(_work.seedHash); + auto cb = [&](void* d) { Ethasher::get()->readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + m_miner->init(p, cb, 32); } - if (m_lastHeader != _header) + if (m_lastWork.headerHash != _work.headerHash) { m_hook->abort(); - static std::random_device s_eng; - auto hh = _header.headerHash(WithoutNonce); - uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_header.boundary() >> 192); - m_miner->search(hh.data(), upper64OfBoundary, *m_hook); + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); + m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); } - m_lastHeader = _header; - - MineInfo mi; - Solution proof; - mi.requirement = log2((double)(u256)_header.boundary()); - mi.best = 0; - - std::this_thread::sleep_for(chrono::milliseconds(_msTimeout)); + m_work = _work; +} - mi.hashes += m_hook->fetchTotal(); - auto found = m_hook->fetchFound(); - if (!found.empty()) - { - for (auto const& n: found) - { - auto result = Ethasher::eval(_header, n); - if (result.value < _header.boundary()) - { - mi.completed = true; - proof = Solution{n, result.mixHash}; - } - } - } - return std::make_pair(mi, proof); +void Ethash::GPUMiner::pause() +{ + m_hook->abort(); } #endif diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 3ea177a9a..f66bc77c9 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -34,7 +34,6 @@ #define FAKE_DAGGER 1 class ethash_cl_miner; -struct ethash_cl_search_hook; namespace dev { @@ -52,8 +51,6 @@ struct MineInfo bool completed = false; }; -class EthashCLHook; - class Ethash { @@ -75,6 +72,7 @@ public: static unsigned instances() { return thread::hardware_concurrency(); } +protected: void kickOff(WorkPackage const& _work) override { stopWorking(); @@ -93,25 +91,29 @@ private: #if ETH_ETHASHCL || !ETH_TRUE -class GPUMiner: public NewMiner +class EthashCLHook; + +class GPUMiner: public Miner { -public: - GPUMiner(ConstructionInfo const& _ci): NewMiner(_ci) - { + friend class EthashCLHook; - } +public: + GPUMiner(ConstructionInfo const& _ci); static unsigned instances() { return 1; } - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; - unsigned defaultTimeout() const override { return 500; } - protected: - Nonce m_last; - BlockInfo m_lastHeader; - Nonce m_mined; - std::unique_ptr m_miner; + void kickOff(WorkPackage const& _work) override; + void pause() override; + +private: + void report(uint64_t _nonce); + std::unique_ptr m_hook; + std::unique_ptr m_miner; + h256 m_minerSeed; + WorkPackage m_lastWork; ///< Work loaded into m_miner. + MineInfo m_info; }; #else From 2fb7883a12b8ffed27fa025cdb1bbd86da7f112f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 20:17:33 +0200 Subject: [PATCH 06/34] Refactor just about everything important in the core. TODO: make compile :-) --- libethcore/Common.cpp | 2 - libethcore/Common.h | 37 +++ libethcore/Ethash.cpp | 241 ++++++++++++++++ libethcore/Ethash.h | 128 +++++++++ libethcore/{Ethasher.cpp => EthashAux.cpp} | 144 ++++------ libethcore/EthashAux.h | 63 +++++ libethcore/Ethasher.h | 113 -------- libethcore/Miner.cpp | 12 - libethcore/Miner.h | 38 ++- libethcore/ProofOfWork.cpp | 220 +-------------- libethcore/ProofOfWork.h | 109 +------- libethereum/BlockChain.cpp | 29 +- libethereum/BlockChain.h | 5 +- libethereum/BlockQueue.cpp | 1 + libethereum/BlockQueue.h | 3 + libethereum/Client.cpp | 309 +++++++++------------ libethereum/Client.h | 67 +++-- libethereum/ClientBase.h | 2 - libethereum/Farm.cpp | 12 - libethereum/Farm.h | 41 +-- libethereum/State.cpp | 14 - libethereum/State.h | 31 +-- libethereum/TransactionQueue.cpp | 1 + libethereum/TransactionQueue.h | 6 +- 24 files changed, 801 insertions(+), 827 deletions(-) create mode 100644 libethcore/Ethash.cpp create mode 100644 libethcore/Ethash.h rename libethcore/{Ethasher.cpp => EthashAux.cpp} (60%) create mode 100644 libethcore/EthashAux.h delete mode 100644 libethcore/Ethasher.h diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 572ade3e2..f0e749aaa 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -22,7 +22,6 @@ #include "Common.h" #include #include -#include "Ethasher.h" #include "Exceptions.h" using namespace std; using namespace dev; @@ -33,7 +32,6 @@ namespace dev namespace eth { -const unsigned c_ethashVersion = c_ethashRevision; const unsigned c_protocolVersion = 60; const unsigned c_minorProtocolVersion = 0; const unsigned c_databaseBaseVersion = 9; diff --git a/libethcore/Common.h b/libethcore/Common.h index ec826d2d1..b5291648b 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -96,5 +96,42 @@ enum class ImportResult BadChain }; +class Signal; + +class Handler +{ +public: + Handler() = default; + Handler(Handler const&) = delete; + ~Handler() { reset(); } + + Handler& operator=(Handler const& _h) = delete; + + void reset(); + +private: + Handler(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} + + unsigned m_i = 0; + Signal* m_s = nullptr; +}; + +/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. +class Signal +{ +public: + using Callback = std::function; + using Callbacks = std::vector; + + Handler add(Callback const& _h) { auto n = m_onReady.size() ? m_onReady.rbegin()->first + 1 : 0; m_onReady[n] = _h; return Handler(n, this); } + + void operator()() { for (auto const& f: m_fire) f.second(); } + +private: + std::map m_fire; +}; + +inline void Handler::reset() { if (m_s) m_s->m_fire->erase(m_i); m_s = nullptr; } + } } diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp new file mode 100644 index 000000000..82e349b4c --- /dev/null +++ b/libethcore/Ethash.cpp @@ -0,0 +1,241 @@ +/* + 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 Ethash.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Ethash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ETH_ETHASHCL || !ETH_TRUE +#include +#endif +#include "BlockInfo.h" +#include "EthashAux.h" +using namespace std; +using namespace std::chrono; + +namespace dev +{ +namespace eth +{ + +const Ethash::WorkPackage Ethash::NullWorkPackage; + +std::string Ethash::name() +{ + return "Ethash"; +} + +unsigned Ethash::revision() +{ + return ETHASH_REVISION; +} + +void Ethash::prep(BlockInfo const& _header) +{ + if (_header.number % ETHASH_EPOCH_LENGTH == 1) + EthashAux::full(bi); +} + +bool Ethash::preVerify(BlockInfo const& _header) +{ + if (_header.number >= ETHASH_EPOCH_LENGTH * 2048) + return false; + + h256 boundary = u256((bigint(1) << 256) / _header.difficulty); + + return ethash_quick_check_difficulty( + _header.headerHash(WithoutNonce).data(), + (uint64_t)(u64)_header.nonce, + _header.mixHash.data(), + boundary.data()); +} + +bool Ethash::verify(BlockInfo const& _header) +{ + bool pre = preVerify(_header); +#if !ETH_DEBUG + if (!pre) + return false; +#endif + + h256 boundary = u256((bigint(1) << 256) / _header.difficulty); + auto result = eval(_header); + bool slow = result.value <= boundary && result.mixHash == _header.mixHash; + +#if ETH_DEBUG || !ETH_TRUE + if (!pre && slow) + { + cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false."; + cwarn << "headerHash:" << _header.headerHash(WithoutNonce); + cwarn << "nonce:" << _header.nonce; + cwarn << "mixHash:" << _header.mixHash; + cwarn << "difficulty:" << _header.difficulty; + cwarn << "boundary:" << boundary; + cwarn << "result.value:" << result.value; + cwarn << "result.mixHash:" << result.mixHash; + } +#endif + + return slow; +} + +void Ethash::CPUMiner::workLoop() +{ + auto tid = std::this_thread::get_id(); + static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); + + uint64_t tryNonce = Nonce::random(s_eng); + ethash_return_value ethashReturn; + + auto p = Ethash::params(m_work.seedHash); + void const* dagPointer = Ethash::full(m_work.headerHash).data(); + uint8_t const* headerHashPointer = m_work.headerHash.data(); + h256 boundary = m_work.boundary(); + unsigned hashCount = 0; + for (; !shouldStop(); tryNonce++, hashCount++) + { + ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); + h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); + if (value <= boundary && submitProof(Solution{value, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) + break; + } +} + +#if ETH_ETHASHCL || !ETH_TRUE + +namespace dev { namespace eth { + +class EthashCLHook: public ethash_cl_miner::search_hook +{ +public: + EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} + + void abort() + { + Guard l(x_all); + if (m_aborted) + return; +// cdebug << "Attempting to abort"; + m_abort = true; + for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) + std::this_thread::sleep_for(chrono::milliseconds(30)); + if (!m_aborted) + cwarn << "Couldn't abort. Abandoning OpenCL process."; + m_aborted = m_abort = false; + } + + uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } + +protected: + virtual bool found(uint64_t const* _nonces, uint32_t _count) override + { +// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); + for (uint32_t i = 0; i < _count; ++i) + { + if (m_owner->found(_nonces[i])) + { + m_aborted = true; + return true; + } + } + return false; + } + + virtual bool searched(uint64_t _startNonce, uint32_t _count) override + { + Guard l(x_all); +// cdebug << "Searched" << _count << "from" << _startNonce; + m_total += _count; + m_last = _startNonce + _count; + if (m_abort) + { + m_aborted = true; + return true; + } + return false; + } + +private: + Mutex x_all; + uint64_t m_total; + uint64_t m_last; + bool m_abort = false; + bool m_aborted = true; + Ethash::GPUMiner* m_owner = nullptr; +}; + +} } + +Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): + Miner(_ci), + m_hook(new EthashCLHook(this)) +{ +} + +void Ethash::GPUMiner::report(uint64_t _nonce) +{ + Nonce n = (Nonce)(u64)_nonce; + Result r = Ethash::eval(m_work.seedHash, m_work.headerHash, n); + if (r.value < m_work.boundary) + return submitProof(Solution{n, r.mixHash}); + return false; +} + +void Ethash::GPUMiner::kickOff(WorkPackage const& _work) +{ + if (!m_miner || m_minerSeed != _work.seedHash) + { + if (m_miner) + m_hook->abort(); + m_miner.reset(new ethash_cl_miner); + auto p = Ethash::params(_work.seedHash); + auto cb = [&](void* d) { EthashAux::readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + m_miner->init(p, cb, 32); + } + if (m_lastWork.headerHash != _work.headerHash) + { + m_hook->abort(); + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); + m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); + } + m_work = _work; +} + +void Ethash::GPUMiner::pause() +{ + m_hook->abort(); +} + +#endif + +} +} diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h new file mode 100644 index 000000000..bdd6bf6c5 --- /dev/null +++ b/libethcore/Ethash.h @@ -0,0 +1,128 @@ +/* + 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 Ethash.h + * @author Gav Wood + * @date 2014 + * + * A proof of work algorithm. + */ + +#pragma once + +#include +#include +#include +#include "Common.h" +#include "BlockInfo.h" +#include "Miner.h" + +class ethash_cl_miner; + +namespace dev +{ +namespace eth +{ + +class Ethash +{ +public: + struct Solution + { + Nonce nonce; + h256 mixHash; + }; + + struct Result + { + h256 value; + h256 mixHash; + }; + + struct WorkPackage + { + h256 boundary; + h256 headerHash; ///< When h256() means "pause until notified a new work package is available". + h256 seedHash; + }; + + static const WorkPackage NullWorkPackage; + + static std::string name(); + static unsigned revision(); + static bool verify(BlockInfo const& _header); + static bool preVerify(BlockInfo const& _header); + static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + static void prep(BlockInfo const& _header); + + class CPUMiner: public Miner, Worker + { + public: + CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} + + static unsigned instances() { return thread::hardware_concurrency(); } + + protected: + void kickOff(WorkPackage const& _work) override + { + stopWorking(); + m_work = _work; + startWorking(); + } + + void pause() override { stopWorking(); } + + private: + void workLoop() override; + + WorkPackage m_work; + MineInfo m_info; + }; + +#if ETH_ETHASHCL || !ETH_TRUE + class EthashCLHook; + class GPUMiner: public Miner + { + friend class EthashCLHook; + + public: + GPUMiner(ConstructionInfo const& _ci); + + static unsigned instances() { return 1; } + + protected: + void kickOff(WorkPackage const& _work) override; + void pause() override; + + private: + void report(uint64_t _nonce); + + std::unique_ptr m_hook; + std::unique_ptr m_miner; + h256 m_minerSeed; + WorkPackage m_lastWork; ///< Work loaded into m_miner. + MineInfo m_info; + }; +#else + using GPUMiner = CPUMiner; +#endif +}; + +using ProofOfWork = Ethash; +using Solution = Ethash::Solution; + +} +} diff --git a/libethcore/Ethasher.cpp b/libethcore/EthashAux.cpp similarity index 60% rename from libethcore/Ethasher.cpp rename to libethcore/EthashAux.cpp index 2118952eb..a6143e264 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/EthashAux.cpp @@ -14,11 +14,13 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file Ethasher.cpp +/** @file EthashAux.cpp * @author Gav Wood * @date 2014 */ +#include "EthashAux.h" + #include #include #include @@ -33,7 +35,6 @@ #include #include #include "BlockInfo.h" -#include "Ethasher.h" using namespace std; using namespace chrono; using namespace dev; @@ -41,15 +42,50 @@ using namespace eth; #define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} -Ethasher* dev::eth::Ethasher::s_this = nullptr; +EthashAux* dev::eth::EthashAux::s_this = nullptr; -Ethasher::~Ethasher() +EthashAux::~EthashAux() { while (!m_lights.empty()) killCache(m_lights.begin()->first); } -void Ethasher::killCache(h256 const& _s) +ethash_params EthashAux::params(BlockInfo const& _header) +{ + return params((unsigned)_header.number); +} + +ethash_params EthashAux::params(unsigned _n) +{ + ethash_params p; + p.cache_size = ethash_get_cachesize(_n); + p.full_size = ethash_get_datasize(_n); + return p; +} + +ethash_params EthashAux::params(h256 const& _seedHash) +{ + RecursiveGuard l(get()->x_this); + unsigned epoch = 0; + try + { + epoch = get()->m_seedHashes.at(_seedHash); + } + catch (...) + { + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} + if (epoch == 2048) + { + std::ostringstream error; + error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); + throw std::invalid_argument(error.str()); + } + get()->m_seedHashes[_seedHash] = epoch; + } + return params(epoch * ETHASH_EPOCH_LENGTH); +} + +void EthashAux::killCache(h256 const& _s) { RecursiveGuard l(x_this); if (m_lights.count(_s)) @@ -59,12 +95,12 @@ void Ethasher::killCache(h256 const& _s) } } -void const* Ethasher::light(BlockInfo const& _header) +void const* EthashAux::light(BlockInfo const& _header) { return light(_header.seedHash()); } -void const* Ethasher::light(h256 const& _seedHash) +void const* EthashAux::light(h256 const& _seedHash) { RecursiveGuard l(x_this); if (!m_lights.count(_header.seedHash())) @@ -75,12 +111,12 @@ void const* Ethasher::light(h256 const& _seedHash) return m_lights[_seedHash]; } -bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest) +bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest) { return full(_header.seedHash(), _dest); } -bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) +bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) { RecursiveGuard l(x_this); if (m_fulls.count(_seedHash) && _dest) @@ -102,9 +138,9 @@ bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(c_ethashRevision, _seedHash); + auto info = rlpList(revision(), _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8)); + std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) { // memofile valid - rename. @@ -136,91 +172,19 @@ bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) return m_fulls[_seedHash]; } -ethash_params Ethasher::params(BlockInfo const& _header) -{ - return params((unsigned)_header.number); -} - -ethash_params Ethasher::params(unsigned _n) -{ - ethash_params p; - p.cache_size = ethash_get_cachesize(_n); - p.full_size = ethash_get_datasize(_n); - return p; -} - -ethash_params Ethasher::params(h256 const& _seedHash) -{ - unsigned epoch = 0; - try - { - epoch = m_seedHashes.at(_seedHash); - } - catch (...) - { - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} - if (epoch == 2048) - { - std::ostringstream error; - error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (c_ethashEpochLength * 2048); - throw std::invalid_argument(error.str()); - } - m_seedHashes[_seedHash] = epoch; - } - return params(epoch * c_ethashEpochLength); -} - -bool Ethasher::verify(BlockInfo const& _header) -{ - if (_header.number >= c_ethashEpochLength * 2048) - return false; - - h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - - bool quick = ethash_quick_check_difficulty( - _header.headerHash(WithoutNonce).data(), - (uint64_t)(u64)_header.nonce, - _header.mixHash.data(), - boundary.data()); - -#if !ETH_DEBUG - if (!quick) - return false; -#endif - - auto result = eval(_header); - bool slow = result.value <= boundary && result.mixHash == _header.mixHash; - -#if ETH_DEBUG - if (!quick && slow) - { - cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false."; - cwarn << "headerHash:" << _header.headerHash(WithoutNonce); - cwarn << "nonce:" << _header.nonce; - cwarn << "mixHash:" << _header.mixHash; - cwarn << "difficulty:" << _header.difficulty; - cwarn << "boundary:" << boundary; - cwarn << "result.value:" << result.value; - cwarn << "result.mixHash:" << result.mixHash; - } -#endif - - return slow; -} - -Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) +Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) { return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); } -Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) +Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) { - auto p = Ethasher::params(_header); + auto p = EthashAux::params(_header); ethash_return_value r; - if (Ethasher::get()->m_fulls.count(_seedHash)) - ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); + if (EthashAux::get()->m_fulls.count(_seedHash)) + ethash_compute_full(&r, EthashAux::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else - ethash_compute_light(&r, Ethasher::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); -// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); + ethash_compute_light(&r, EthashAux::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); +// cdebug << "EthashAux::eval sha3(cache):" << sha3(EthashAux::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h new file mode 100644 index 000000000..bfd01a594 --- /dev/null +++ b/libethcore/EthashAux.h @@ -0,0 +1,63 @@ +/* + 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 EthashAux.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include "Ethash.h" + +namespace dev +{ +namespace eth{ + +class EthashAux +{ + EthashAux() {} + ~EthashAux(); + + static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; } + + using LightType = void const*; + using FullType = void const*; + + static ethash_params params(BlockInfo const& _header); + static ethash_params params(h256 const& _seedHash); + static ethash_params params(unsigned _n); + static LightType light(BlockInfo const& _header); + static LightType light(h256 const& _header); + static bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); + static bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); + + static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } + static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce); + static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); + +private: + void killCache(h256 const& _s); + + static Ethash* s_this; + RecursiveMutex x_this; + + std::map m_lights; + std::map m_fulls; + std::map m_seedHashes; +}; + +} +} diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h deleted file mode 100644 index f57c2f4f6..000000000 --- a/libethcore/Ethasher.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - 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 Ethasher.h - * @author Gav Wood - * @date 2014 - * - * ProofOfWork algorithm. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include // TODO: REMOVE once everything merged into this class and an opaque API can be provided. -static const unsigned c_ethashRevision = ETHASH_REVISION; -static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH; -#include "Common.h" -#include "BlockInfo.h" - -namespace dev -{ -namespace eth -{ - -class Ethasher -{ -public: - Ethasher() {} - ~Ethasher(); - - static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } - - using LightType = void const*; - using FullType = void const*; - - LightType light(BlockInfo const& _header); - LightType light(h256 const& _header); - bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); - bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); - - static ethash_params params(BlockInfo const& _header); - static ethash_params params(h256 const& _seedHash); - static ethash_params params(unsigned _n); - - struct Result - { - h256 value; - h256 mixHash; - }; - - static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } - static Result eval(BlockInfo const& _header, Nonce const& _nonce); - static Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); - static bool verify(BlockInfo const& _header); - - class Miner - { - public: - Miner(BlockInfo const& _header): - m_headerHash(_header.headerHash(WithoutNonce)), - m_params(Ethasher::params(_header)), - m_datasetPointer(Ethasher::get()->full(_header).data()) - {} - - inline h256 mine(uint64_t _nonce) - { - ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); -// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer); - return h256(m_ethashReturn.result, h256::ConstructFromPointer); - } - - inline h256 lastMixHash() const - { - return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); - } - - private: - ethash_return_value m_ethashReturn; - h256 m_headerHash; - ethash_params m_params; - void const* m_datasetPointer; - }; - -private: - void killCache(h256 const& _s); - - static Ethasher* s_this; - RecursiveMutex x_this; - std::map m_lights; - std::map m_fulls; - std::map m_seedHashes; -}; - -} -} diff --git a/libethcore/Miner.cpp b/libethcore/Miner.cpp index d6f15866f..e69de29bb 100644 --- a/libethcore/Miner.cpp +++ b/libethcore/Miner.cpp @@ -1,12 +0,0 @@ -#include "Miner.h" - -Miner::Miner() -{ - -} - -Miner::~Miner() -{ - -} - diff --git a/libethcore/Miner.h b/libethcore/Miner.h index a7f17e565..e7157e660 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -15,9 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Miner.h - * @author Alex Leverington * @author Gav Wood - * @date 2014 + * @date 2015 */ #pragma once @@ -28,7 +27,6 @@ #include #include #include -#include "State.h" namespace dev { @@ -36,15 +34,17 @@ namespace dev namespace eth { -struct WorkPackage +struct MineInfo { - h256 boundary; - h256 headerHash; ///< When h256() means "pause until notified a new work package is available". - h256 seedHash; + MineInfo() = default; + MineInfo(bool _completed): completed(_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; + unsigned hashes = 0; + bool completed = false; }; -static const WorkPackage NullWorkPackage; - /** * @brief Describes the progress of a mining operation. */ @@ -58,30 +58,40 @@ struct MiningProgress unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; +template class Miner; + /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple * miner threads. */ -class FarmFace +template class FarmFace { public: + using WorkPackage = typename PoW::WorkPackage; + using Solution = typename PoW::Solution; + using Miner = Miner; + /** * @brief Called from a Miner to note a WorkPackage has a solution. * @param _p The solution. * @param _wp The WorkPackage that the Solution is for. + * @param _finder The miner that found it. * @return true iff the solution was good (implying that mining should be . */ - virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; + virtual bool submitProof(Solution const& _p, WorkPackage const& _wp, Miner* _finder) = 0; }; /** * @brief A miner - a member and adoptee of the Farm. */ -class Miner +template class Miner { public: - using ConstructionInfo = std::pair; + using ConstructionInfo = std::pair*, unsigned>; + using WorkPackage = typename PoW::WorkPackage; + using Solution = typename PoW::Solution; + using FarmFace = FarmFace; Miner(ConstructionInfo const& _ci): m_farm(_ci.first), @@ -124,7 +134,7 @@ protected: * @param _s The solution. * @return true if the solution was correct and that the miner should pause. */ - bool submitProof(ProofOfWork::Solution const& _s) + bool submitProof(Solution const& _s) { if (m_farm) { diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index 97488ee35..ec910f7f2 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -19,224 +19,6 @@ * @date 2014 */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if ETH_ETHASHCL || !ETH_TRUE -#include -#endif -#include "BlockInfo.h" -#include "Ethasher.h" #include "ProofOfWork.h" using namespace std; -using namespace std::chrono; - -namespace dev -{ -namespace eth -{ - -void Ethash::CPUMiner::workLoop() -{ - Solution solution; - - class Miner - { - public: - Miner(BlockInfo const& _header): - m_headerHash(_header.headerHash(WithoutNonce)), - m_params(Ethasher::params(_header)), - m_datasetPointer(Ethasher::get()->full(_header).data()) - {} - - inline h256 mine(uint64_t _nonce) - { - ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); -// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer); - return h256(m_ethashReturn.result, h256::ConstructFromPointer); - } - - inline h256 lastMixHash() const - { - return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); - } - - private: - ethash_return_value m_ethashReturn; - h256 m_headerHash; - ethash_params m_params; - void const* m_datasetPointer; - }; - - Ethasher::Miner m(_header); - - std::pair ret; - auto tid = std::this_thread::get_id(); - static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); - uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng)); - - h256 boundary = _header.boundary(); - ret.first.requirement = log2((double)(u256)boundary); - - // 2^ 0 32 64 128 256 - // [--------*-------------------------] - // - // evaluate until we run out of time - auto startTime = std::chrono::steady_clock::now(); - double best = 1e99; // high enough to be effectively infinity :) - Solution result; - unsigned hashCount = 0; - for (; !shouldStop(); tryNonce++, hashCount++) - { - h256 val(m.mine(tryNonce)); - best = std::min(best, log2((double)(u256)val)); - if (val <= boundary) - { - if (submitProof(solution)) - return; - } - } - ret.first.hashes = hashCount; - ret.first.best = best; - ret.second = result; - - return; -} - -#if ETH_ETHASHCL || !ETH_TRUE - -/* -class ethash_cl_miner -{ -public: - struct search_hook - { - // reports progress, return true to abort - virtual bool found(uint64_t const* nonces, uint32_t count) = 0; - virtual bool searched(uint64_t start_nonce, uint32_t count) = 0; - }; - - ethash_cl_miner(); - - bool init(ethash_params const& params, const uint8_t seed[32], unsigned workgroup_size = 64); - - void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count); - void search(uint8_t const* header, uint64_t target, search_hook& hook); -}; -*/ - -namespace dev { namespace eth { -class EthashCLHook: public ethash_cl_miner::search_hook -{ -public: - EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} - - void abort() - { - Guard l(x_all); - if (m_aborted) - return; -// cdebug << "Attempting to abort"; - m_abort = true; - for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) - std::this_thread::sleep_for(chrono::milliseconds(30)); - if (!m_aborted) - cwarn << "Couldn't abort. Abandoning OpenCL process."; - m_aborted = m_abort = false; - } - - uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } - -protected: - virtual bool found(uint64_t const* _nonces, uint32_t _count) override - { -// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); - for (uint32_t i = 0; i < _count; ++i) - { - if (m_owner->found(_nonces[i])) - { - m_aborted = true; - return true; - } - } - return false; - } - - virtual bool searched(uint64_t _startNonce, uint32_t _count) override - { - Guard l(x_all); -// cdebug << "Searched" << _count << "from" << _startNonce; - m_total += _count; - m_last = _startNonce + _count; - if (m_abort) - { - m_aborted = true; - return true; - } - return false; - } - -private: - Mutex x_all; - uint64_t m_total; - uint64_t m_last; - bool m_abort = false; - bool m_aborted = true; - Ethash::GPUMiner* m_owner = nullptr; -}; - -} } - -Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): - Miner(_ci), - m_hook(new EthashCLHook(this)) -{ -} - -void Ethash::GPUMiner::report(uint64_t _nonce) -{ - Nonce n = (Nonce)(u64)_nonce; - Ethasher::Result r = Ethasher::eval(m_work.seedHash, m_work.headerHash, n); - if (r.value < m_work.boundary) - return submitProof(Solution{n, r.mixHash}); - return false; -} - -void Ethash::GPUMiner::kickOff(WorkPackage const& _work) -{ - if (!m_miner || m_minerSeed != _work.seedHash) - { - if (m_miner) - m_hook->abort(); - m_miner.reset(new ethash_cl_miner); - auto p = Ethasher::params(_work.seedHash); - auto cb = [&](void* d) { Ethasher::get()->readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; - m_miner->init(p, cb, 32); - } - if (m_lastWork.headerHash != _work.headerHash) - { - m_hook->abort(); - uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); - m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); - } - m_work = _work; -} - -void Ethash::GPUMiner::pause() -{ - m_hook->abort(); -} - -#endif - -} -} +using namespace dev; diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index f66bc77c9..764207aef 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -18,112 +18,29 @@ * @author Gav Wood * @date 2014 * - * ProofOfWork algorithm. Or not. + * Determines the PoW algorithm. */ #pragma once -#include -#include -#include -#include -#include "Common.h" -#include "BlockInfo.h" -#include "Miner.h" - -#define FAKE_DAGGER 1 - -class ethash_cl_miner; +#include "Ethash.h" namespace dev { namespace eth { -struct MineInfo -{ - MineInfo() = default; - MineInfo(bool _completed): completed(_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; - unsigned hashes = 0; - bool completed = false; -}; - -class Ethash -{ - -public: - -struct Solution -{ - Nonce nonce; - h256 mixHash; -}; - -static bool verify(BlockInfo const& _header); -static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } - -class CPUMiner: public Miner, Worker -{ -public: - CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} - - static unsigned instances() { return thread::hardware_concurrency(); } - -protected: - void kickOff(WorkPackage const& _work) override - { - stopWorking(); - m_work = _work; - startWorking(); - } - - void pause() override { stopWorking(); } - -private: - void workLoop() override; - - WorkPackage m_work; - MineInfo m_info; -}; - -#if ETH_ETHASHCL || !ETH_TRUE - -class EthashCLHook; - -class GPUMiner: public Miner -{ - friend class EthashCLHook; - -public: - GPUMiner(ConstructionInfo const& _ci); - - static unsigned instances() { return 1; } - -protected: - void kickOff(WorkPackage const& _work) override; - void pause() override; - -private: - void report(uint64_t _nonce); - - std::unique_ptr m_hook; - std::unique_ptr m_miner; - h256 m_minerSeed; - WorkPackage m_lastWork; ///< Work loaded into m_miner. - MineInfo m_info; -}; - -#else - -using GPUMiner = CPUMiner; - -#endif - -}; - +/** + * The proof of work algorithm base type. + * + * Must implement a basic templated interface, including: + * typename Result + * typename Solution + * typename CPUMiner + * typename GPUMiner + * void assignResult(BlockInfo&, Result) + * and a few others. TODO + */ using ProofOfWork = Ethash; } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index bf2bce4fe..0ff0a3628 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -231,8 +231,7 @@ void BlockChain::rebuild(std::string const& _path, std::function(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); BlockInfo bi(b); - if (bi.number % c_ethashEpochLength == 1) - Ethasher::get()->full(bi); + ProofOfWork::prep(bi); if (bi.parentHash != lastHash) { @@ -307,14 +306,8 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st try { auto r = import(block, _stateDB); - bool isOld = true; - for (auto const& h: r.first) - if (h == r.second) - isOld = false; - else if (isOld) - dead.push_back(h); - else - fresh.push_back(h); + fresh += r.first; + dead += r.second; } catch (UnknownParent) { @@ -334,7 +327,7 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st return make_tuple(fresh, dead, _bq.doneDrain(badBlocks)); } -pair BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force) noexcept +ImportRoute BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force) noexcept { try { @@ -347,7 +340,7 @@ pair BlockChain::attemptImport(bytes const& _block, OverlayDB const } } -pair BlockChain::import(bytes const& _block, OverlayDB const& _db, Aversion _force) +ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Aversion _force) { //@tidy This is a behemoth of a method - could do to be split into a few smaller ones. @@ -626,7 +619,17 @@ pair BlockChain::import(bytes const& _block, OverlayDB const& _db, cnote << "checkBest:" << checkBest; #endif - return make_pair(route, common); + h256s fresh; + h256s dead; + bool isOld = true; + for (auto const& h: route) + if (h == common) + isOld = false; + else if (isOld) + dead.push_back(h); + else + fresh.push_back(h); + return make_pair(fresh, dead); } void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 765e00b03..cdff566fb 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -68,6 +68,7 @@ ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; using UncleHashes = h256s; +using ImportRoute = std::pair; enum { ExtraDetails = 0, @@ -108,11 +109,11 @@ public: /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. - std::pair attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks) noexcept; + ImportRoute attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks) noexcept; /// Import block into disk-backed DB /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. - std::pair import(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks); + ImportRoute import(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks); /// Returns true if the given block is known (though not necessarily a part of the canon chain). bool isKnown(h256 const& _hash) const; diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 6f8c64827..e9dd99cd1 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -102,6 +102,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) m_readySet.insert(h); noteReadyWithoutWriteGuard(h); + m_onReady(); return ImportResult::Success; } } diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index ce0582db2..f5cdf7ab5 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -83,6 +83,8 @@ public: /// Get some infomration on the current status. BlockQueueStatus status() const { ReadGuard l(m_lock); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; } + template Handler onReady(T const& _t) { return m_onReady.add(_t); } + private: void noteReadyWithoutWriteGuard(h256 _b); void notePresentWithoutWriteGuard(bytesConstRef _block); @@ -95,6 +97,7 @@ private: std::multimap> m_unknown; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. std::multimap m_future; ///< Set of blocks that are not yet valid. std::set m_knownBad; ///< Set of blocks that we know will never be valid. + Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. }; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 278c02fa8..9c6b87293 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -117,7 +117,7 @@ void BasicGasPricer::update(BlockChain const& _bc) } } -Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): +Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): Worker("eth"), m_vc(_dbPath), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), @@ -126,14 +126,14 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_preMine(m_stateDB, BaseState::CanonGenesis), m_postMine(m_stateDB) { + m_tqReady = m_tq->onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq->onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_gp->update(m_bc); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); - if (_miners > -1) - setMiningThreads(_miners); - else - setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); @@ -142,7 +142,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for startWorking(); } -Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): +Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): Worker("eth"), m_vc(_dbPath), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), @@ -151,14 +151,14 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string c m_preMine(m_stateDB), m_postMine(m_stateDB) { + m_tq->onReady([=](){ this->onTransactionQueueReady(); }); + m_bq->onReady([=](){ this->onBlockQueueReady(); }); + m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_gp->update(m_bc); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); - if (_miners > -1) - setMiningThreads(_miners); - else - setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); @@ -229,8 +229,6 @@ void Client::killChain() doWork(); - setMiningThreads(0); - startWorking(); if (wasMining) startMining(); @@ -271,26 +269,6 @@ static string filtersToString(T const& _fs) return ret.str(); } -void Client::noteChanged(h256Set const& _filters) -{ - Guard l(x_filtersWatches); - if (_filters.size()) - cnote << "noteChanged(" << filtersToString(_filters) << ")"; - // accrue all changes left in each filter into the watches. - for (auto& w: m_watches) - if (_filters.count(w.second.id)) - { - cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???"); - if (m_filters.count(w.second.id)) // Normal filtering watch - w.second.changes += m_filters.at(w.second.id).changes; - else // Special ('pending'/'latest') watch - w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); - } - // clear the filters now. - for (auto& i: m_filters) - i.second.changes.clear(); -} - void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) { Guard l(x_filtersWatches); @@ -342,22 +320,6 @@ void Client::setForceMining(bool _enable) m.noteStateChange(); } -void Client::setMiningThreads(unsigned _threads) -{ - stopMining(); - auto t = _threads ? _threads : thread::hardware_concurrency(); -#if ETH_ETHASHCL || !ETH_TRUE - if (m_turboMining) - t = 1; -#endif - WriteGuard l(x_localMiners); - m_localMiners.clear(); - m_localMiners.resize(t); - unsigned i = 0; - for (auto& m: m_localMiners) - m.setup(this, i++); -} - MineProgress Client::miningProgress() const { MineProgress ret; @@ -452,160 +414,161 @@ pair Client::getWork() return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); } -bool Client::submitWork(ProofOfWork::Solution const& _proof) +bool Client::submitWork(ProofOfWork::Solution const& _solution) { - Guard l(x_remoteMiner); - return m_remoteMiner.submitWork(_proof); + bytes newBlock; + { + WriteGuard l(x_stateDB); + if (!m_postMine.completeMine(_solution)) + return false; + newBlock = m_postMine.blockData(); + } + + ImportRoute ir = m_bc.attemptImport(newBlock, m_stateDB); + if (!ir.first.empty()) + onChainChanged(ir); + return true; } -void Client::doWork() +void Client::syncBlockQueue() { - // TODO: Use condition variable rather than polling. - - bool stillGotWork = false; - - cworkin << "WORK"; - h256Set changeds; - - auto maintainMiner = [&](OldMiner& m) - { - if (m.isComplete()) - { - // TODO: enable a short-circuit option since we mined it. will need to get the end state from the miner. - auto lm = dynamic_cast(&m); - h256s hs; - h256 c; - if (false && lm && !m_verifyOwnBlocks) - { - // TODO: implement - //m_bc.attemptImport(m_blockData(), m_stateDB, lm->state()); - // TODO: derive hs from lm->state() - } - else - { - cwork << "CHAIN <== postSTATE"; - WriteGuard l(x_stateDB); - tie(hs, c) = m_bc.attemptImport(m.blockData(), m_stateDB); - } - if (hs.size()) - { - for (auto const& h: hs) - if (h != c) - appendFromNewBlock(h, changeds); - changeds.insert(ChainChangedFilter); - } - for (auto& m: m_localMiners) - m.noteStateChange(); - } - }; - { - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - maintainMiner(m); - } - { - Guard l(x_remoteMiner); - maintainMiner(m_remoteMiner); - } + ImportResult ir; - // Synchronise state to block chain. - // This should remove any transactions on our queue that are included within our state. - // It also guarantees that the state reflects the longest (valid!) chain on the block chain. - // This might mean reverting to an earlier state and replaying some blocks, or, (worst-case: - // if there are no checkpoints before our fork) reverting to the genesis block and replaying - // all blocks. - // Resynchronise state with block chain & trans - bool resyncStateNeeded = false; { WriteGuard l(x_stateDB); cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; x_stateDB.unlock(); - h256s fresh; - h256s dead; - bool sgw; - tie(fresh, dead, sgw) = m_bc.sync(m_bq, db, 100); - - // insert transactions that we are declaring the dead part of the chain - for (auto const& h: dead) - { - clog(ClientNote) << "Dead block:" << h.abridged(); - for (auto const& t: m_bc.transactions(h)) - { - clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); - m_tq.import(t); - } - } - // remove transactions from m_tq nicely rather than relying on out of date nonce later on. - for (auto const& h: fresh) - { - clog(ClientChat) << "Live block:" << h.abridged(); - for (auto const& th: m_bc.transactionHashes(h)) - { - clog(ClientNote) << "Safely dropping transaction " << th.abridged(); - m_tq.drop(th); - } - } + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); - stillGotWork = stillGotWork | sgw; - if (!fresh.empty()) - { - for (auto i: fresh) - appendFromNewBlock(i, changeds); - changeds.insert(ChainChangedFilter); - } x_stateDB.lock(); if (fresh.size()) m_stateDB = db; + } + + if (!ir.first.empty()) + onChainChanged(ir); + return true; +} + +void Client::syncTransactionQueue() +{ + // returns TransactionReceipts, once for each transaction. + cwork << "postSTATE <== TQ"; + TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + if (newPendingReceipts.size()) + { + for (size_t i = 0; i < newPendingReceipts.size(); i++) + appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); - cwork << "preSTATE <== CHAIN"; - if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) + changeds.insert(PendingChangedFilter); + + if (isMining()) + cnote << "Additional transaction ready: Restarting mining operation."; + resyncStateNeeded = true; + if (auto h = m_host.lock()) + h->noteNewTransactions(); + } +} + +void Client::onChainChanged(ImportRoute const& _ir) +{ + // insert transactions that we are declaring the dead part of the chain + for (auto const& h: _ir.second) + { + clog(ClientNote) << "Dead block:" << h.abridged(); + for (auto const& t: m_bc.transactions(h)) { - if (isMining()) - cnote << "New block on chain: Restarting mining operation."; - m_postMine = m_preMine; - resyncStateNeeded = true; - changeds.insert(PendingChangedFilter); - // TODO: Move transactions pending from m_postMine back to transaction queue. + clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); + m_tq.import(t); } + } - // returns TransactionReceipts, once for each transaction. - cwork << "postSTATE <== TQ"; - TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); - if (newPendingReceipts.size()) + // remove transactions from m_tq nicely rather than relying on out of date nonce later on. + for (auto const& h: _ir.first) + { + clog(ClientChat) << "Live block:" << h.abridged(); + for (auto const& th: m_bc.transactionHashes(h)) { - for (size_t i = 0; i < newPendingReceipts.size(); i++) - appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); - - changeds.insert(PendingChangedFilter); - - if (isMining()) - cnote << "Additional transaction ready: Restarting mining operation."; - resyncStateNeeded = true; - if (auto h = m_host.lock()) - h->noteNewTransactions(); + clog(ClientNote) << "Safely dropping transaction " << th.abridged(); + m_tq.drop(th); } } - if (!changeds.empty()) - if (auto h = m_host.lock()) - h->noteNewBlocks(); + if (auto h = m_host.lock()) + h->noteNewBlocks(); + + h256Set changeds; + for (auto const& h: _ir.first) + if (h != _ir.second) + appendFromNewBlock(h, changeds); + changeds.insert(ChainChangedFilter); + noteChanged(changeds); + + // RESTART MINING - if (resyncStateNeeded) + // LOCKS NEEDED? + Guard l(x_stateDB); + cwork << "preSTATE <== CHAIN"; + if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - m.noteStateChange(); + if (isMining()) + cnote << "New block on chain: Restarting mining operation."; + m_postMine = m_preMine; + resyncStateNeeded = true; + changeds.insert(PendingChangedFilter); + + m_postMine.commitToMine(m_bc); + m_farm.setWork(m_postMine.info()); } +} - cwork << "noteChanged" << changeds.size() << "items"; - noteChanged(changeds); - cworkout << "WORK"; +void Client::noteChanged(h256Set const& _filters) +{ + Guard l(x_filtersWatches); + if (_filters.size()) + cnote << "noteChanged(" << filtersToString(_filters) << ")"; + // accrue all changes left in each filter into the watches. + for (auto& w: m_watches) + if (_filters.count(w.second.id)) + { + cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???"); + if (m_filters.count(w.second.id)) // Normal filtering watch + w.second.changes += m_filters.at(w.second.id).changes; + else // Special ('pending'/'latest') watch + w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); + } + // clear the filters now. + for (auto& i: m_filters) + i.second.changes.clear(); +} + +void Client::doWork() +{ + // TODO: Use condition variable rather than this rubbish. + + Guard l(x_fakeSignalSystemState); + + if (m_syncTransactionQueue) + { + m_syncTransactionQueue = false; + syncTransactionQueue(); + } + + if (m_syncBlockQueue) + { + m_syncBlockQueue = false; + syncBlockQueue(); + } + + checkWatchGarbage(); - if (!stillGotWork) - this_thread::sleep_for(chrono::milliseconds(100)); + this_thread::sleep_for(chrono::milliseconds(20)); +} +void Client::checkWatchGarbage() +{ if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) { // watches garbage collection diff --git a/libethereum/Client.h b/libethereum/Client.h index 9af501f74..8eac2e577 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -42,6 +42,7 @@ #include "CommonNet.h" #include "Miner.h" #include "ABI.h" +#include "Farm.h" #include "ClientBase.h" namespace dev @@ -122,7 +123,7 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C /** * @brief Main API hub for interfacing with Ethereum. */ -class Client: public MinerHost, public ClientBase, Worker +class Client: public ClientBase, Worker { friend class OldMiner; @@ -132,8 +133,7 @@ public: p2p::Host* _host, std::string const& _dbPath = std::string(), WithExisting _forceAction = WithExisting::Trust, - u256 _networkId = 0, - int _miners = -1 + u256 _networkId = 0 ); explicit Client( @@ -141,8 +141,7 @@ public: std::shared_ptr _gpForAdoption, // pass it in with new. std::string const& _dbPath = std::string(), WithExisting _forceAction = WithExisting::Trust, - u256 _networkId = 0, - int _miners = -1 + u256 _networkId = 0 ); /// Destructor. @@ -191,31 +190,31 @@ public: /// Are we allowed to GPU mine? bool turboMining() const { return m_turboMining; } /// Enable/disable GPU mining. - void setTurboMining(bool _enable = true) { bool was = isMining(); stopMining(); m_turboMining = _enable; setMiningThreads(0); if (was) startMining(); } + void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); } - /// Stops mining and sets the number of mining threads (0 for automatic). - void setMiningThreads(unsigned _threads = 0) override; - /// Get the effective number of mining threads. - unsigned miningThreads() const override { ReadGuard l(x_localMiners); return m_localMiners.size(); } /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { startWorking(); { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.start(); } } + void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); } /// Stop mining. /// NOT thread-safe - void stopMining() override { { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.stop(); } } - /// Are we mining now? - bool isMining() const override { { ReadGuard l(x_localMiners); if (!m_localMiners.empty() && m_localMiners[0].isRunning()) return true; } return false; } + void stopMining() override { m_farm.stop(); } /// Are we mining now? + bool isMining() const override { return m_farm.isMining(); } + /// The hashrate... uint64_t hashrate() const override; /// Check the progress of the mining. - MineProgress miningProgress() const override; + MiningProgress miningProgress() const override; /// Get and clear the mining history. std::list miningHistory(); /// Update to the latest transactions and get hash of the current block to be mined minus the /// nonce (the 'work hash') and the difficulty to be met. virtual std::pair getWork() override; - /// Submit the proof for the proof-of-work. + + /** @brief Submit the proof for the proof-of-work. + * @param _s A valid solution. + * @return true if the solution was indeed valid and accepted. + */ virtual bool submitWork(ProofOfWork::Solution const& _proof) override; // Debug stuff: @@ -261,10 +260,23 @@ private: /// Called when Worker is exiting. virtual void doneWorking(); - /// Overrides for being a mining host. - virtual void setupState(State& _s); - virtual bool turbo() const { return m_turboMining; } - virtual bool force() const { return m_forceMining; } + /// Magically called when the chain has changed. An import route is provided. + /// Called by either submitWork() or in our main thread through syncBlockQueue(). + void onChainChanged(ImportRoute const& _ir); + + /// Signal handler for when the block queue needs processing. + void syncBlockQueue(); + + /// Signal handler for when the block queue needs processing. + void syncTransactionQueue(); + + /// Magically called when m_tq needs syncing. Be nice and don't block. + void onTransactionQueueReady() { Guard l(x_fakeSignalSystemState); m_syncTransactionQueue = true; } + + /// Magically called when m_tq needs syncing. Be nice and don't block. + void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; } + + void checkWatchGarbage(); VersionChecker m_vc; ///< Dummy object to check & update the protocol version. CanonBlockChain m_bc; ///< Maintains block database. @@ -278,17 +290,24 @@ private: std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. + Farm m_farm; ///< Our mining farm. mutable Mutex x_remoteMiner; ///< The remote miner lock. RemoteMiner m_remoteMiner; ///< The remote miner. - std::vector m_localMiners; ///< The in-process miners. - mutable SharedMutex x_localMiners; ///< The in-process miners lock. - bool m_paranoia = false; ///< Should we be paranoid about our state? + Handler m_tqReady; + Handler m_bqReady; + 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? - bool m_verifyOwnBlocks = true; ///< Should be verify blocks that we mined? + bool m_paranoia = false; ///< Should we be paranoid about our state? mutable std::chrono::system_clock::time_point m_lastGarbageCollection; + ///< When did we last both doing GC on the watches? + + // TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables) + mutable Mutex x_fakeSignalSystemState; + bool m_syncTransactionQueue = false; + bool m_syncBlockQueue = false; }; } diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ae6d27578..4b3cc5002 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -142,8 +142,6 @@ public: /// TODO: consider moving it to a separate interface - virtual void setMiningThreads(unsigned _threads) override { (void)_threads; BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::setMiningThreads")); } - virtual unsigned miningThreads() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningThreads")); } virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); } virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); } diff --git a/libethereum/Farm.cpp b/libethereum/Farm.cpp index 639e4efcf..e69de29bb 100644 --- a/libethereum/Farm.cpp +++ b/libethereum/Farm.cpp @@ -1,12 +0,0 @@ -#include "Farm.h" - -Farm::Farm() -{ - -} - -Farm::~Farm() -{ - -} - diff --git a/libethereum/Farm.h b/libethereum/Farm.h index a49038f0d..55d1aa8df 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -14,10 +14,9 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file Miner.h - * @author Alex Leverington +/** @file Farm.h * @author Gav Wood - * @date 2014 + * @date 2015 */ #pragma once @@ -28,7 +27,8 @@ #include #include #include -#include "Miner.h" +#include +#include namespace dev { @@ -41,8 +41,8 @@ namespace eth * Miners ask for work, then submit proofs * @threadsafe */ -template -class Farm: public FarmFace +template +class Farm: public FarmFace { public: /** @@ -65,13 +65,13 @@ public: * @brief (Re)start miners for CPU only. * @returns true if started properly. */ - bool startCPU() { return start(); } + bool startCPU() { return start(); } /** * @brief (Re)start miners for GPU only. * @returns true if started properly. */ - bool startGPU() { start(); } + bool startGPU() { start(); } /** * @brief Stop all mining activities. @@ -82,20 +82,24 @@ public: m_miners.clear(); } + bool isMining() const + { + ReadGuard l(x_miners); + return !m_miners.empty(); + } + /** * @brief Get information on the progress of mining this work package. * @return The progress with mining so far. */ - MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } + MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; } -protected: - // TO BE REIMPLEMENTED BY THE SUBCLASS /** * @brief Provides a valid header based upon that received previously with setWork(). * @param _bi The now-valid header. * @return true if the header was good and that the Farm should pause until more work is submitted. */ - virtual bool submitHeader(BlockInfo const& _bi) = 0; + void onSolutionFound(function _handler) { m_onSolutionFound = _handler; } private: /** @@ -104,16 +108,15 @@ private: * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ - bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp, NewMiner* _m) override + bool submitProof(Solution const& _s, WorkPackage const& _wp, Miner* _m) override { if (_wp.headerHash != m_work.headerHash) return false; - ProofOfWork::assignResult(_p, m_header); - if (submitHeader(m_header)) + if (m_onSolutionFound && m_onSolutionFound(_s)) { ReadGuard l(x_miners); - for (std::shared_ptr const& m: m_miners) + for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->pause(); m_work.headerHash = h256(); @@ -139,14 +142,16 @@ private: } mutable SharedMutex x_miners; - std::vector> m_miners; + std::vector> m_miners; mutable SharedMutex x_progress; - MineProgress m_progress; + MiningProgress m_progress; mutable SharedMutex x_work; WorkPackage m_work; BlockInfo m_header; + + function m_onSolutionFound; }; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index e77565837..75618eb5f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -856,20 +856,6 @@ void State::commitToMine(BlockChain const& _bc) m_committedToMine = true; } -bool State::completeMine(ProofOfWork::Solution const& _nonce) -{ - ProofOfWork::assignResult(_nonce, m_currentBlock); - -// if (!m_pow.verify(m_currentBlock)) -// return false; - - cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock); - - completeMine(); - - return true; -} - void State::completeMine() { cdebug << "Completing mine!"; diff --git a/libethereum/State.h b/libethereum/State.h index b327378a1..36a505481 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "TransactionQueue.h" @@ -162,30 +163,19 @@ public: /// Pass in a solution to the proof-of-work. /// @returns true iff the given nonce is a proof-of-work for this State's block. - bool completeMine(ProofOfWork::Solution const& _result); - - /// Attempt to find valid nonce for block that this state represents. - /// This function is thread-safe. You can safely have other interactions with this object while it is happening. - /// @param _msTimeout Timeout before return in milliseconds. - /// @returns Information on the mining. - template MineInfo mine(ProofOfWork* _pow) + template + bool completeMine(typename PoW::Solution const& _result) { - // Update difficulty according to timestamp. - m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); + PoW::assignResult(_result, m_currentBlock); - MineInfo ret; - typename ProofOfWork::Solution r; - std::tie(ret, r) = _pow->mine(m_currentBlock, _pow->defaultTimeout(), true); + // if (!m_pow.verify(m_currentBlock)) + // return false; - if (!ret.completed) - m_currentBytes.clear(); - else - { - ProofOfWork::assignResult(r, m_currentBlock); - cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock); - } + cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << PoW::verify(m_currentBlock); - return ret; + completeMine(); + + return true; } /** Commit to DB and build the final block if the previous call to mine()'s result is completion. @@ -369,7 +359,6 @@ private: /// Debugging only. Good for checking the Trie is in shape. void paranoia(std::string const& _when, bool _enforceRefs = false) const; - OverlayDB m_db; ///< Our overlay for the state tree. SecureTrieDB m_state; ///< Our state tree, as an OverlayDB DB. Transactions m_transactions; ///< The current list of transactions that we've included in the state. diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 7c72f53e8..506de2d2f 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -53,6 +53,7 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb if (_cb) m_callbacks[h] = _cb; ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); + m_onReady(); } catch (Exception const& _e) { diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index ad093b4e5..e18f47e80 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -26,7 +26,7 @@ #include #include #include -#include "libethcore/Common.h" +#include #include "Transaction.h" namespace dev @@ -60,13 +60,15 @@ public: void noteGood(std::pair const& _t); void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } + template Handler onReady(T const& _t) { return m_onReady.add(_t); } private: mutable boost::shared_mutex m_lock; ///< General lock. std::set m_known; ///< Hashes of transactions in both sets. std::map m_current; ///< Map of SHA3(tx) to tx. std::multimap> m_unknown; ///< 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_callbacks; ///< Called once + std::map> m_callbacks; ///< Called once. + Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. }; } From be2480d85ef8ffcd83066620a7ace4c42326e3dc Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 00:08:52 +0200 Subject: [PATCH 07/34] Compile fixes. --- libethcore/Common.h | 37 +++++++++++++------------------ libethcore/Ethash.cpp | 40 +++++++++++++++------------------- libethcore/Ethash.h | 12 ++++++---- libethcore/EthashAux.cpp | 36 +++++++++++++++--------------- libethcore/EthashAux.h | 6 +++-- libethcore/Miner.h | 16 ++++++++------ libethereum/Client.h | 2 +- libethereum/Farm.h | 2 +- libethereum/Interface.h | 5 ----- libethereum/TransactionQueue.h | 2 +- mix/MixClient.cpp | 10 --------- mix/MixClient.h | 2 -- 12 files changed, 75 insertions(+), 95 deletions(-) diff --git a/libethcore/Common.h b/libethcore/Common.h index b5291648b..4d01055f1 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -96,42 +96,35 @@ enum class ImportResult BadChain }; -class Signal; - -class Handler +/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. +class Signal { public: - Handler() = default; - Handler(Handler const&) = delete; - ~Handler() { reset(); } + class HandlerAux + { + friend class Signal; - Handler& operator=(Handler const& _h) = delete; + public: + ~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; } - void reset(); - -private: - Handler(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} + private: + HandlerAux(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} - unsigned m_i = 0; - Signal* m_s = nullptr; -}; + unsigned m_i = 0; + Signal* m_s = nullptr; + }; -/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. -class Signal -{ -public: using Callback = std::function; - using Callbacks = std::vector; - Handler add(Callback const& _h) { auto n = m_onReady.size() ? m_onReady.rbegin()->first + 1 : 0; m_onReady[n] = _h; return Handler(n, this); } + std::shared_ptr add(Callback const& _h) { auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1); m_fire[n] = _h; return std::shared_ptr(new HandlerAux(n, this)); } void operator()() { for (auto const& f: m_fire) f.second(); } private: - std::map m_fire; + std::map m_fire; }; -inline void Handler::reset() { if (m_s) m_s->m_fire->erase(m_i); m_s = nullptr; } +using Handler = std::shared_ptr; } } diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 82e349b4c..f3b4d1b18 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -62,7 +62,7 @@ unsigned Ethash::revision() void Ethash::prep(BlockInfo const& _header) { if (_header.number % ETHASH_EPOCH_LENGTH == 1) - EthashAux::full(bi); + EthashAux::full(_header); } bool Ethash::preVerify(BlockInfo const& _header) @@ -88,7 +88,7 @@ bool Ethash::verify(BlockInfo const& _header) #endif h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - auto result = eval(_header); + auto result = EthashAux::eval(_header); bool slow = result.value <= boundary && result.mixHash == _header.mixHash; #if ETH_DEBUG || !ETH_TRUE @@ -111,29 +111,27 @@ bool Ethash::verify(BlockInfo const& _header) void Ethash::CPUMiner::workLoop() { auto tid = std::this_thread::get_id(); - static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); + static std::mt19937_64 s_eng((time(0) + std::hash()(tid))); - uint64_t tryNonce = Nonce::random(s_eng); + uint64_t tryNonce = (uint64_t)(u64)Nonce::random(s_eng); ethash_return_value ethashReturn; - auto p = Ethash::params(m_work.seedHash); - void const* dagPointer = Ethash::full(m_work.headerHash).data(); + auto p = EthashAux::params(m_work.seedHash); + void const* dagPointer = EthashAux::full(m_work.headerHash).data(); uint8_t const* headerHashPointer = m_work.headerHash.data(); - h256 boundary = m_work.boundary(); + h256 boundary = m_work.boundary; unsigned hashCount = 0; for (; !shouldStop(); tryNonce++, hashCount++) { ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); - if (value <= boundary && submitProof(Solution{value, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) + if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) break; } } #if ETH_ETHASHCL || !ETH_TRUE -namespace dev { namespace eth { - class EthashCLHook: public ethash_cl_miner::search_hook { public: @@ -148,8 +146,8 @@ public: m_abort = true; for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) std::this_thread::sleep_for(chrono::milliseconds(30)); - if (!m_aborted) - cwarn << "Couldn't abort. Abandoning OpenCL process."; +// if (!m_aborted) +// cwarn << "Couldn't abort. Abandoning OpenCL process."; m_aborted = m_abort = false; } @@ -161,7 +159,7 @@ protected: // cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); for (uint32_t i = 0; i < _count; ++i) { - if (m_owner->found(_nonces[i])) + if (m_owner->report(_nonces[i])) { m_aborted = true; return true; @@ -193,19 +191,17 @@ private: Ethash::GPUMiner* m_owner = nullptr; }; -} } - Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), m_hook(new EthashCLHook(this)) { } -void Ethash::GPUMiner::report(uint64_t _nonce) +bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; - Result r = Ethash::eval(m_work.seedHash, m_work.headerHash, n); - if (r.value < m_work.boundary) + Result r = EthashAux::eval(m_lastWork.seedHash, m_lastWork.headerHash, n); + if (r.value < m_lastWork.boundary) return submitProof(Solution{n, r.mixHash}); return false; } @@ -217,17 +213,17 @@ void Ethash::GPUMiner::kickOff(WorkPackage const& _work) if (m_miner) m_hook->abort(); m_miner.reset(new ethash_cl_miner); - auto p = Ethash::params(_work.seedHash); - auto cb = [&](void* d) { EthashAux::readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + auto p = EthashAux::params(_work.seedHash); + auto cb = [&](void* d) { EthashAux::full(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); } if (m_lastWork.headerHash != _work.headerHash) { m_hook->abort(); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); - m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); + m_miner->search(_work.headerHash.data(), upper64OfBoundary, *m_hook); } - m_work = _work; + m_lastWork = _work; } void Ethash::GPUMiner::pause() diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index bdd6bf6c5..b87f06549 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "Common.h" #include "BlockInfo.h" #include "Miner.h" @@ -37,9 +38,13 @@ namespace dev namespace eth { +class EthashCLHook; + class Ethash { public: + using Miner = GenericMiner; + struct Solution { Nonce nonce; @@ -73,7 +78,7 @@ public: public: CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} - static unsigned instances() { return thread::hardware_concurrency(); } + static unsigned instances() { return std::thread::hardware_concurrency(); } protected: void kickOff(WorkPackage const& _work) override @@ -93,10 +98,9 @@ public: }; #if ETH_ETHASHCL || !ETH_TRUE - class EthashCLHook; class GPUMiner: public Miner { - friend class EthashCLHook; + friend class dev::eth::EthashCLHook; public: GPUMiner(ConstructionInfo const& _ci); @@ -108,7 +112,7 @@ public: void pause() override; private: - void report(uint64_t _nonce); + bool report(uint64_t _nonce); std::unique_ptr m_hook; std::unique_ptr m_miner; diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index a6143e264..969310dac 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -102,13 +102,13 @@ void const* EthashAux::light(BlockInfo const& _header) void const* EthashAux::light(h256 const& _seedHash) { - RecursiveGuard l(x_this); - if (!m_lights.count(_header.seedHash())) + RecursiveGuard l(get()->x_this); + if (!get()->m_lights.count(_seedHash)) { ethash_params p = params(_seedHash); - m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); + get()->m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); } - return m_lights[_seedHash]; + return get()->m_lights[_seedHash]; } bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest) @@ -118,14 +118,14 @@ bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest) bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) { - RecursiveGuard l(x_this); - if (m_fulls.count(_seedHash) && _dest) + RecursiveGuard l(get()->x_this); + if (get()->m_fulls.count(_seedHash) && _dest) { - assert(m_fulls.size() <= _dest.size()); - m_fulls.at(_seedHash).copyTo(_dest); - return; + assert(get()->m_fulls.size() <= _dest.size()); + get()->m_fulls.at(_seedHash).copyTo(_dest); + return _dest; } - if (!m_fulls.count(_seedHash)) + if (!get()->m_fulls.count(_seedHash)) { // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. /* if (!m_fulls.empty()) @@ -138,7 +138,7 @@ bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(revision(), _seedHash); + auto info = rlpList(Ethash::revision(), _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) @@ -147,8 +147,8 @@ bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) boost::filesystem::rename(oldMemoFile, memoFile); } - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); + ETH_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); + ETH_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); ethash_params p = params(_seedHash); assert(!_dest || _dest.size() >= p.full_size); // must be big enough. @@ -162,14 +162,14 @@ bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) r = _dest; else r = bytesRef(new byte[p.full_size], p.full_size); - ethash_prep_full(r, &p, light(_seedHash)); + ethash_prep_full(r.data(), &p, light(_seedHash)); writeFile(memoFile, r); } if (_dest) return _dest; - m_fulls[_seedHash] = r; + get()->m_fulls[_seedHash] = r; } - return m_fulls[_seedHash]; + return get()->m_fulls[_seedHash]; } Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) @@ -179,12 +179,12 @@ Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) { - auto p = EthashAux::params(_header); + auto p = EthashAux::params(_seedHash); ethash_return_value r; if (EthashAux::get()->m_fulls.count(_seedHash)) ethash_compute_full(&r, EthashAux::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else ethash_compute_light(&r, EthashAux::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); // cdebug << "EthashAux::eval sha3(cache):" << sha3(EthashAux::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); - return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; + return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index bfd01a594..aec1089a2 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -28,7 +28,7 @@ namespace eth{ class EthashAux { - EthashAux() {} +public: ~EthashAux(); static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; } @@ -49,9 +49,11 @@ class EthashAux static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); private: + EthashAux() {} + void killCache(h256 const& _s); - static Ethash* s_this; + static EthashAux* s_this; RecursiveMutex x_this; std::map m_lights; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index e7157e660..9372c06b1 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -58,19 +58,19 @@ struct MiningProgress unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; -template class Miner; +template class GenericMiner; /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple * miner threads. */ -template class FarmFace +template class GenericFarmFace { public: using WorkPackage = typename PoW::WorkPackage; using Solution = typename PoW::Solution; - using Miner = Miner; + using Miner = GenericMiner; /** * @brief Called from a Miner to note a WorkPackage has a solution. @@ -85,15 +85,15 @@ public: /** * @brief A miner - a member and adoptee of the Farm. */ -template class Miner +template class GenericMiner { public: - using ConstructionInfo = std::pair*, unsigned>; using WorkPackage = typename PoW::WorkPackage; using Solution = typename PoW::Solution; - using FarmFace = FarmFace; + using FarmFace = GenericFarmFace; + using ConstructionInfo = std::pair; - Miner(ConstructionInfo const& _ci): + GenericMiner(ConstructionInfo const& _ci): m_farm(_ci.first), m_index(_ci.second) {} @@ -144,6 +144,8 @@ protected: return true; } + WorkPackage const& work() const { return m_work; } + private: FarmFace* m_farm = nullptr; unsigned m_index; diff --git a/libethereum/Client.h b/libethereum/Client.h index 8eac2e577..a136033e5 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -290,7 +290,7 @@ private: std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. - Farm m_farm; ///< Our mining farm. + GenericFarm m_farm; ///< Our mining farm. mutable Mutex x_remoteMiner; ///< The remote miner lock. RemoteMiner m_remoteMiner; ///< The remote miner. diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 55d1aa8df..39988c8d7 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -42,7 +42,7 @@ namespace eth * @threadsafe */ template -class Farm: public FarmFace +class GenericFarm: public GenericFarmFace { public: /** diff --git a/libethereum/Interface.h b/libethereum/Interface.h index cf2e7f5ea..134bed53b 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -171,11 +171,6 @@ public: /// Get the coinbase address. virtual Address address() const = 0; - /// Stops mining and sets the number of mining threads (0 for automatic). - virtual void setMiningThreads(unsigned _threads = 0) = 0; - /// Get the effective number of mining threads. - virtual unsigned miningThreads() const = 0; - /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread virtual void startMining() = 0; diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index e18f47e80..c3df00d25 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -48,7 +48,7 @@ class TransactionQueue public: using ImportCallback = std::function; - ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx); } + ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx, _cb); } ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback()); void drop(h256 _txHash); diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index dd7047d26..254ceb325 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -372,16 +372,6 @@ void MixClient::setAddress(Address _us) m_state.setAddress(_us); } -void MixClient::setMiningThreads(unsigned _threads) -{ - m_miningThreads = _threads; -} - -unsigned MixClient::miningThreads() const -{ - return m_miningThreads; -} - void MixClient::startMining() { //no-op diff --git a/mix/MixClient.h b/mix/MixClient.h index a5ecbf465..0d1cce2bc 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -63,8 +63,6 @@ public: dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict); void setAddress(Address _us) override; - void setMiningThreads(unsigned _threads) override; - unsigned miningThreads() const override; void startMining() override; void stopMining() override; bool isMining() const override; From db3e0831c3940fc0108a61741b5391b453822e8b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 00:34:42 +0200 Subject: [PATCH 08/34] Various compile fixes. --- libethcore/Ethash.cpp | 9 ++ libethcore/Ethash.h | 6 +- libethereum/BlockChain.cpp | 3 +- libethereum/BlockChain.h | 2 +- libethereum/Client.cpp | 7 +- libethereum/Client.h | 24 +----- libethereum/Farm.h | 4 +- libethereum/Interface.h | 4 +- libethereum/Miner.cpp | 94 --------------------- libethereum/Miner.h | 166 ------------------------------------- 10 files changed, 22 insertions(+), 297 deletions(-) diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index f3b4d1b18..ac54ebe4d 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -59,6 +59,15 @@ unsigned Ethash::revision() return ETHASH_REVISION; } +Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) +{ + WorkPackage ret; + ret.boundary = _bi.boundary(); + ret.headerHash = _bi.headerHash(WithNonce); + ret.seedHash = _bi.seedHash(); + return ret; +} + void Ethash::prep(BlockInfo const& _header) { if (_header.number % ETHASH_EPOCH_LENGTH == 1) diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index b87f06549..458cb4e6c 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -68,10 +68,11 @@ public: static std::string name(); static unsigned revision(); + static void prep(BlockInfo const& _header); static bool verify(BlockInfo const& _header); static bool preVerify(BlockInfo const& _header); + static WorkPackage package(BlockInfo const& _header); static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } - static void prep(BlockInfo const& _header); class CPUMiner: public Miner, Worker { @@ -125,8 +126,5 @@ public: #endif }; -using ProofOfWork = Ethash; -using Solution = Ethash::Solution; - } } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 0ff0a3628..b73a79c49 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "GenesisInfo.h" #include "State.h" @@ -336,7 +335,7 @@ ImportRoute BlockChain::attemptImport(bytes const& _block, OverlayDB const& _sta catch (...) { cwarn << "Unexpected exception! Could not import block!" << boost::current_exception_diagnostic_information(); - return make_pair(h256s(), h256()); + return make_pair(h256s(), h256s()); } } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index cdff566fb..8b4a3dce4 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -68,7 +68,7 @@ ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; using UncleHashes = h256s; -using ImportRoute = std::pair; +using ImportRoute = std::pair; enum { ExtraDetails = 0, diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 9c6b87293..532c092ff 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -404,14 +404,17 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 return ret; } -pair Client::getWork() +ProofOfWork::WorkPackage Client::getWork() { Guard l(x_remoteMiner); + BlockInfo bi; { ReadGuard l(x_stateDB); m_remoteMiner.update(m_postMine, m_bc); + m_postMine.commitToMine(m_bc); + bi = m_postMine.info(); } - return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); + return ProofOfWork::package(bi); } bool Client::submitWork(ProofOfWork::Solution const& _solution) diff --git a/libethereum/Client.h b/libethereum/Client.h index a136033e5..9f6bbfeb6 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -73,28 +73,6 @@ private: std::string m_path; }; -class RemoteMiner: public OldMiner -{ -public: - RemoteMiner() {} - - void update(State const& _provisional, BlockChain const& _bc) { m_state = _provisional; m_state.commitToMine(_bc); } - - h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); } - u256 const& difficulty() const { return m_state.info().difficulty; } - - bool submitWork(ProofOfWork::Solution const& _result) { return (m_isComplete = m_state.completeMine(_result)); } - - virtual bool isComplete() const override { return m_isComplete; } - virtual bytes const& blockData() const { return m_state.blockData(); } - - virtual void noteStateChange() override {} - -private: - bool m_isComplete = false; - State m_state; -}; - class BasicGasPricer: public GasPricer { public: @@ -209,7 +187,7 @@ public: /// Update to the latest transactions and get hash of the current block to be mined minus the /// nonce (the 'work hash') and the difficulty to be met. - virtual std::pair getWork() override; + virtual ProofOfWork::WorkPackage getWork() override; /** @brief Submit the proof for the proof-of-work. * @param _s A valid solution. diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 39988c8d7..137c137d3 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -53,9 +53,7 @@ public: { WriteGuard l(x_work); m_header = _bi; - m_work.boundary = _bi.boundary(); - m_work.headerHash = _bi.headerHash(WithNonce); - m_work.seedHash = _bi.seedHash(); + m_work = PoW::package(m_header); ReadGuard l(x_miners); for (auto const& m: miners) m->setWork(m_work); diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 134bed53b..0ab81728b 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -26,11 +26,11 @@ #include #include #include +#include #include "LogFilter.h" #include "Transaction.h" #include "AccountDiff.h" #include "BlockDetails.h" -#include "Miner.h" namespace dev { @@ -188,7 +188,7 @@ public: virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0; /// Check the progress of the mining. - virtual MineProgress miningProgress() const = 0; + virtual MiningProgress miningProgress() const = 0; protected: int m_default = PendingBlock; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index b386fe868..e69de29bb 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -1,94 +0,0 @@ -/* - 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 Miner.cpp - * @author Gav Wood - * @author Giacomo Tazzari - * @date 2014 - */ - -#include "Miner.h" - -#include -#include "State.h" -using namespace std; -using namespace dev; -using namespace dev::eth; - -OldMiner::~OldMiner() {} - -LocalMiner::LocalMiner(MinerHost* _host, unsigned _id) -{ - setup(_host, _id); -} - -void LocalMiner::setup(MinerHost* _host, unsigned _id) -{ - AsyncMiner::setup(_host, _id); - setName("miner-" + toString(m_id)); - m_pow.reset(_host->turbo() ? new Ethash : (Ethash*)new EthashCPU); -} - -void LocalMiner::doWork() -{ - // Do some mining. - if (m_miningStatus != Waiting && m_miningStatus != Mined) - { - if (m_miningStatus == Preparing) - { - m_host->setupState(m_mineState); - if (m_host->force() || m_mineState.pending().size()) - m_miningStatus = Mining; - else - m_miningStatus = Waiting; - - { - Guard l(x_mineInfo); - m_mineProgress.best = (double)-1; - m_mineProgress.hashes = 0; - m_mineProgress.ms = 0; - } - } - - if (m_miningStatus == Mining) - { - // Mine for a while. - MineInfo mineInfo = m_mineState.mine(m_pow.get()); - - { - Guard l(x_mineInfo); - m_mineProgress.best = min(m_mineProgress.best, mineInfo.best); - m_mineProgress.current = mineInfo.best; - m_mineProgress.requirement = mineInfo.requirement; - m_mineProgress.ms += 100; - m_mineProgress.hashes += mineInfo.hashes; - m_mineHistory.push_back(mineInfo); - } - if (mineInfo.completed) - { - m_mineState.completeMine(); - m_host->onComplete(); - m_miningStatus = Mined; - } - else - m_host->onProgressed(); - } - } - else - { - this_thread::sleep_for(chrono::milliseconds(100)); - } -} diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 3abf93770..e69de29bb 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -1,166 +0,0 @@ -/* - 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 Miner.h - * @author Alex Leverington - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "State.h" - -namespace dev -{ - -namespace eth -{ - -/** - * @brief Class for hosting one or more Miners. - * @warning Must be implemented in a threadsafe manner since it will be called from multiple - * miner threads. - */ -class MinerHost -{ -public: - virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. - virtual void onProgressed() {} ///< Called once some progress has been made. - virtual void onComplete() {} ///< Called once a block is found. - virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions. - virtual bool turbo() const = 0; ///< @returns true iff the Miner should use GPU if possible. -}; - -class OldMiner -{ -public: - virtual ~OldMiner(); - - virtual void noteStateChange() = 0; - virtual bool isComplete() const = 0; - virtual bytes const& blockData() const = 0; -}; - -class AsyncMiner: public OldMiner -{ -public: - /// Null constructor. - AsyncMiner(): m_host(nullptr) {} - - /// Constructor. - AsyncMiner(MinerHost* _host, unsigned _id = 0): m_host(_host), m_id(_id) {} - - /// Setup its basics. - void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; m_id = _id; } - - /// Start mining. - virtual void start() {} - - /// Stop mining. - virtual void stop() {} - - /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). - virtual bool isRunning() const { return false; } - -protected: - MinerHost* m_host = nullptr; ///< Our host. - unsigned m_id = 0; ///< Our unique id. -}; - -/** - * @brief Implements Miner. - * To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the - * State object according to the host. Use isRunning() to determine if the miner has been start()ed. - * Use isComplete() to determine if the miner has finished mining. - * - * blockData() can be used to retrieve the complete block, ready for insertion into the BlockChain. - * - * Information on the mining can be queried through miningProgress() and miningHistory(). - * @threadsafe - * @todo Signal Miner to restart once with condition variables. - */ -class LocalMiner: public AsyncMiner, Worker -{ -public: - /// Null constructor. - LocalMiner() {} - - /// Constructor. - LocalMiner(MinerHost* _host, unsigned _id = 0); - - /// Move-constructor. - LocalMiner(LocalMiner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); } - - /// Move-assignment. - LocalMiner& operator=(LocalMiner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); return *this; } - - /// Destructor. Stops miner. - ~LocalMiner() { stop(); } - - /// Setup its basics. - void setup(MinerHost* _host, unsigned _id = 0); - - /// Start mining. - void start() { startWorking(); } - - /// Stop mining. - void stop() { stopWorking(); } - - /// Call to notify Miner of a state change. - virtual void noteStateChange() override { m_miningStatus = Preparing; } - - /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). - bool isRunning() const override { return isWorking(); } - - /// @returns true if mining is complete. - virtual bool isComplete() const override { return m_miningStatus == Mined; } - - /// @returns the internal State object. - virtual bytes const& blockData() const override { return m_mineState.blockData(); } - - /// Check the progress of the mining. - MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; } - - /// Get and clear the mining history. - std::list miningHistory() { Guard l(x_mineInfo); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; } - - /// @returns the state on which we mined. - State const& state() const { return m_mineState; } - -private: - /// Do some work on the mining. - virtual void doWork(); - - enum MiningStatus { Waiting, Preparing, Mining, Mined, Stopping, Stopped }; - MiningStatus m_miningStatus = Waiting; ///< TODO: consider mutex/atomic variable. - State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine. - std::unique_ptr m_pow; ///< Our miner. - - mutable Mutex x_mineInfo; ///< Lock for the mining progress & history. - MineProgress m_mineProgress; ///< What's our progress? - std::list m_mineHistory; ///< What the history of our mining? -}; - -} -} From 405653c250105daa0d2881d7f427343722aa0256 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 01:38:04 +0200 Subject: [PATCH 09/34] Lots of build fixes. Now minimal version builds ok. --- alethzero/MainWin.cpp | 2 +- eth/main.cpp | 11 +- libethash-cl/ethash_cl_miner.cpp | 2 + libethash-cl/ethash_cl_miner.h | 2 + libethcore/Common.cpp | 3 +- libethcore/Common.h | 3 - libethcore/Ethash.cpp | 14 ++- libethcore/Ethash.h | 6 +- libethereum/Client.cpp | 124 ++++++++++------------ libethereum/Client.h | 10 +- libethereum/ClientBase.cpp | 4 +- libethereum/ClientBase.h | 43 ++++---- libethereum/Farm.h | 33 ++++-- libethereum/Interface.h | 2 +- libethereum/Miner.cpp | 0 libethereum/Miner.h | 0 libweb3jsonrpc/WebThreeStubServerBase.cpp | 5 +- libwebthree/WebThree.cpp | 4 +- libwebthree/WebThree.h | 3 +- 19 files changed, 143 insertions(+), 128 deletions(-) delete mode 100644 libethereum/Miner.cpp delete mode 100644 libethereum/Miner.h diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 374829a61..03d216a17 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -164,7 +164,7 @@ Main::Main(QWidget *parent) : statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->blockCount); - ui->blockCount->setText(QString("PV%2 D%3 H%4 v%5").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(c_ethashVersion).arg(dev::Version)); + ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(ProofOfWork::name()).arg(ProofOfWork::revision()).arg(dev::Version)); connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); diff --git a/eth/main.cpp b/eth/main.cpp index e9af192f9..05826dc0f 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #if ETH_READLINE #include #include @@ -44,7 +45,6 @@ #include #include #endif -#include #include "BuildInfo.h" using namespace std; using namespace dev; @@ -210,7 +210,7 @@ void doInitDAG(unsigned _n) BlockInfo bi; bi.number = _n; cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl; - Ethasher::get()->full(bi); + Ethash::prep(bi); exit(0); } @@ -271,7 +271,6 @@ int main(int argc, char** argv) /// Mining params unsigned mining = ~(unsigned)0; - int miners = -1; bool forceMining = false; KeyPair us = KeyPair::create(); Address coinbase = us.address(); @@ -477,8 +476,6 @@ int main(int argc, char** argv) g_logVerbosity = atoi(argv[++i]); else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) peers = atoi(argv[++i]); - else if ((arg == "-t" || arg == "--miners") && i + 1 < argc) - miners = atoi(argv[++i]); else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) { string m = argv[++i]; @@ -537,9 +534,7 @@ int main(int argc, char** argv) killChain, nodeMode == NodeMode::Full ? set{"eth", "shh"} : set(), netPrefs, - &nodesState, - miners - ); + &nodesState); if (mode == OperationMode::DAGInit) doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 0)); diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 96f1fa582..016d8af58 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -50,6 +50,8 @@ static void add_definition(std::string& source, char const* id, unsigned value) source.insert(source.begin(), buf, buf + strlen(buf)); } +ethash_cl_miner::search_hook::~search_hook() {} + ethash_cl_miner::ethash_cl_miner() : m_opencl_1_1() { diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index e478c739f..d3d9f0223 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -12,6 +12,8 @@ class ethash_cl_miner public: struct search_hook { + virtual ~search_hook(); // always a virtual destructor for a class with virtuals. + // reports progress, return true to abort virtual bool found(uint64_t const* nonces, uint32_t count) = 0; virtual bool searched(uint64_t start_nonce, uint32_t count) = 0; diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index f0e749aaa..a0ceb389e 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -23,6 +23,7 @@ #include #include #include "Exceptions.h" +#include "ProofOfWork.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -41,7 +42,7 @@ const unsigned c_databaseVersionModifier = 1; const unsigned c_databaseVersionModifier = 0; #endif -const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (c_ethashVersion << 9); +const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (ProofOfWork::revision() << 9); vector> const& units() { diff --git a/libethcore/Common.h b/libethcore/Common.h index 4d01055f1..bb704405a 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -41,9 +41,6 @@ extern const unsigned c_minorProtocolVersion; /// Current database version. extern const unsigned c_databaseVersion; -/// Current database version. -extern const unsigned c_ethashVersion; - /// User-friendly string representation of the amount _b in wei. std::string formatBalance(bigint const& _b); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index ac54ebe4d..03c7a3654 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -70,8 +70,7 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) void Ethash::prep(BlockInfo const& _header) { - if (_header.number % ETHASH_EPOCH_LENGTH == 1) - EthashAux::full(_header); + EthashAux::full(_header); } bool Ethash::preVerify(BlockInfo const& _header) @@ -206,6 +205,12 @@ Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): { } +Ethash::GPUMiner::~GPUMiner() +{ + delete m_hook; + delete m_miner; +} + bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; @@ -221,7 +226,10 @@ void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { if (m_miner) m_hook->abort(); - m_miner.reset(new ethash_cl_miner); + + delete m_miner; + m_miner = new ethash_cl_miner; + auto p = EthashAux::params(_work.seedHash); auto cb = [&](void* d) { EthashAux::full(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 458cb4e6c..1a7d82149 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -105,6 +105,7 @@ public: public: GPUMiner(ConstructionInfo const& _ci); + ~GPUMiner(); static unsigned instances() { return 1; } @@ -115,8 +116,9 @@ public: private: bool report(uint64_t _nonce); - std::unique_ptr m_hook; - std::unique_ptr m_miner; + EthashCLHook* m_hook; + ethash_cl_miner* m_miner; + h256 m_minerSeed; WorkPackage m_lastWork; ///< Work loaded into m_miner. MineInfo m_info; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 532c092ff..17fb0bf0d 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -126,9 +126,9 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_preMine(m_stateDB, BaseState::CanonGenesis), m_postMine(m_stateDB) { - m_tqReady = m_tq->onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); - m_bqReady = m_bq->onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); - m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); m_gp->update(m_bc); @@ -151,9 +151,9 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string c m_preMine(m_stateDB), m_postMine(m_stateDB) { - m_tq->onReady([=](){ this->onTransactionQueueReady(); }); - m_bq->onReady([=](){ this->onBlockQueueReady(); }); - m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); m_gp->update(m_bc); @@ -210,7 +210,7 @@ void Client::killChain() m_tq.clear(); m_bq.clear(); - m_localMiners.clear(); + m_farm.stop(); m_preMine = State(); m_postMine = State(); @@ -248,11 +248,7 @@ void Client::clearPending() m_postMine = m_preMine; } - { - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - m.noteStateChange(); - } + startMining(); noteChanged(changeds); } @@ -315,34 +311,23 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) void Client::setForceMining(bool _enable) { m_forceMining = _enable; - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - m.noteStateChange(); + startMining(); } -MineProgress Client::miningProgress() const +MiningProgress Client::miningProgress() const { - MineProgress ret; - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - ret.combine(m.miningProgress()); - return ret; + return MiningProgress(); } uint64_t Client::hashrate() const { - uint64_t ret; - ReadGuard l(x_localMiners); - for (LocalMiner const& m: m_localMiners) - ret += m.miningProgress().hashes / m.miningProgress().ms; - return ret / 1000; + return 0; } std::list Client::miningHistory() { std::list ret; - - ReadGuard l(x_localMiners); +/* ReadGuard l(x_localMiners); if (m_localMiners.empty()) return ret; ret = m_localMiners[0].miningHistory(); @@ -353,11 +338,11 @@ std::list Client::miningHistory() auto li = l.begin(); for (; ri != ret.end() && li != l.end(); ++ri, ++li) ri->combine(*li); - } + }*/ return ret; } -void Client::setupState(State& _s) +/*void Client::setupState(State& _s) { { ReadGuard l(x_stateDB); @@ -378,7 +363,7 @@ void Client::setupState(State& _s) } else _s.commitToMine(m_bc); -} +}*/ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from) { @@ -406,15 +391,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 ProofOfWork::WorkPackage Client::getWork() { - Guard l(x_remoteMiner); - BlockInfo bi; - { - ReadGuard l(x_stateDB); - m_remoteMiner.update(m_postMine, m_bc); - m_postMine.commitToMine(m_bc); - bi = m_postMine.info(); - } - return ProofOfWork::package(bi); + return ProofOfWork::package(m_miningInfo); } bool Client::submitWork(ProofOfWork::Solution const& _solution) @@ -422,7 +399,7 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) bytes newBlock; { WriteGuard l(x_stateDB); - if (!m_postMine.completeMine(_solution)) + if (!m_postMine.completeMine(_solution)) return false; newBlock = m_postMine.blockData(); } @@ -435,7 +412,7 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) void Client::syncBlockQueue() { - ImportResult ir; + ImportRoute ir; { WriteGuard l(x_stateDB); @@ -446,30 +423,33 @@ void Client::syncBlockQueue() tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); x_stateDB.lock(); - if (fresh.size()) - m_stateDB = db; + if (ir.first.empty()) + return; + m_stateDB = db; } - - if (!ir.first.empty()) - onChainChanged(ir); - return true; + onChainChanged(ir); } void Client::syncTransactionQueue() { // returns TransactionReceipts, once for each transaction. cwork << "postSTATE <== TQ"; + + h256Set changeds; TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); if (newPendingReceipts.size()) { for (size_t i = 0; i < newPendingReceipts.size(); i++) appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); - changeds.insert(PendingChangedFilter); - if (isMining()) - cnote << "Additional transaction ready: Restarting mining operation."; - resyncStateNeeded = true; + // TODO: Tell farm about new transaction (i.e. restartProofOfWork mining). + onPostStateChanged(); + + // Tell watches about the new transactions. + noteChanged(changeds); + + // Tell network about the new transactions. if (auto h = m_host.lock()) h->noteNewTransactions(); } @@ -504,27 +484,41 @@ void Client::onChainChanged(ImportRoute const& _ir) h256Set changeds; for (auto const& h: _ir.first) - if (h != _ir.second) - appendFromNewBlock(h, changeds); + appendFromNewBlock(h, changeds); changeds.insert(ChainChangedFilter); - noteChanged(changeds); // RESTART MINING - // LOCKS NEEDED? - Guard l(x_stateDB); - cwork << "preSTATE <== CHAIN"; - if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) + // LOCKS REALLY NEEDED? { - if (isMining()) - cnote << "New block on chain: Restarting mining operation."; - m_postMine = m_preMine; - resyncStateNeeded = true; - changeds.insert(PendingChangedFilter); + ReadGuard l(x_stateDB); + if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) + { + if (isMining()) + cnote << "New block on chain."; + + m_postMine = m_preMine; + changeds.insert(PendingChangedFilter); + + x_stateDB.unlock(); + onPostStateChanged(); + x_stateDB.lock(); + } + } + + noteChanged(changeds); +} +void Client::onPostStateChanged() +{ + cnote << "Post state changed: Restarting mining..."; + { + WriteGuard l(x_stateDB); m_postMine.commitToMine(m_bc); - m_farm.setWork(m_postMine.info()); + m_miningInfo = m_postMine.info(); } + + m_farm.setWork(m_miningInfo); } void Client::noteChanged(h256Set const& _filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index 9f6bbfeb6..2235c4459 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -40,7 +40,6 @@ #include "TransactionQueue.h" #include "State.h" #include "CommonNet.h" -#include "Miner.h" #include "ABI.h" #include "Farm.h" #include "ClientBase.h" @@ -103,8 +102,6 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C */ class Client: public ClientBase, Worker { - friend class OldMiner; - public: /// New-style Constructor. explicit Client( @@ -254,6 +251,10 @@ private: /// Magically called when m_tq needs syncing. Be nice and don't block. void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; } + /// Called when the post state has changed (i.e. when more transactions are in it or we're mining on a new block). + /// This updates m_miningInfo. + void onPostStateChanged(); + void checkWatchGarbage(); VersionChecker m_vc; ///< Dummy object to check & update the protocol version. @@ -265,12 +266,11 @@ private: OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_preMine; ///< The present state of the client. State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). + BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine). std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. GenericFarm m_farm; ///< Our mining farm. - mutable Mutex x_remoteMiner; ///< The remote miner lock. - RemoteMiner m_remoteMiner; ///< The remote miner. Handler m_tqReady; Handler m_bqReady; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 5a0aef7c3..ecc0fb7f5 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -20,10 +20,12 @@ * @date 2015 */ -#include #include "ClientBase.h" + +#include #include "BlockChain.h" #include "Executive.h" +#include "State.h" using namespace std; using namespace dev; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 4b3cc5002..15dbbf1ab 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -25,6 +25,7 @@ #include #include "Interface.h" #include "LogFilter.h" +#include "TransactionQueue.h" namespace dev { @@ -60,15 +61,15 @@ struct ClientWatch }; struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; -#define cwatch dev::LogOutputStream() +#define cwatch LogOutputStream() struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; struct WorkOutChannel: public LogChannel { static const char* name() { return "() -#define cworkin dev::LogOutputStream() -#define cworkout dev::LogOutputStream() +#define cwork LogOutputStream() +#define cworkin LogOutputStream() +#define cworkout LogOutputStream() -class ClientBase: public dev::eth::Interface +class ClientBase: public Interface { public: ClientBase() {} @@ -110,18 +111,18 @@ public: virtual LocalisedLogEntries checkWatch(unsigned _watchId) override; virtual h256 hashFromNumber(BlockNumber _number) const override; - virtual eth::BlockInfo blockInfo(h256 _hash) const override; - virtual eth::BlockDetails blockDetails(h256 _hash) const override; - virtual eth::Transaction transaction(h256 _transactionHash) const override; - virtual eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; - virtual eth::Transactions transactions(h256 _blockHash) const override; - virtual eth::TransactionHashes transactionHashes(h256 _blockHash) const override; - virtual eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; - virtual eth::UncleHashes uncleHashes(h256 _blockHash) const override; + virtual BlockInfo blockInfo(h256 _hash) const override; + virtual BlockDetails blockDetails(h256 _hash) const override; + virtual Transaction transaction(h256 _transactionHash) const override; + virtual Transaction transaction(h256 _blockHash, unsigned _i) const override; + virtual Transactions transactions(h256 _blockHash) const override; + virtual TransactionHashes transactionHashes(h256 _blockHash) const override; + virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const override; + virtual UncleHashes uncleHashes(h256 _blockHash) const override; virtual unsigned transactionCount(h256 _blockHash) const override; virtual unsigned uncleCount(h256 _blockHash) const override; virtual unsigned number() const override; - virtual eth::Transactions pending() const override; + virtual Transactions pending() const override; virtual h256s pendingHashes() const override; void injectBlock(bytes const& _block); @@ -142,13 +143,13 @@ public: /// TODO: consider moving it to a separate interface - virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); } - virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } - virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); } - virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::hashrate")); } - virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); } - virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } - virtual bool submitWork(eth::ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } + virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); } + virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::stopMining")); } + virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::isMining")); } + virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); } + virtual MiningProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); } + virtual ProofOfWork::WorkPackage getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::getWork")); } + virtual bool submitWork(ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::submitWork")); } State asOf(BlockNumber _h) const; diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 137c137d3..09c7f0e78 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace dev { @@ -45,17 +46,25 @@ template class GenericFarm: public GenericFarmFace { public: + using WorkPackage = typename PoW::WorkPackage; + using Solution = typename PoW::Solution; + using Miner = GenericMiner; + /** * @brief Sets the current mining mission. * @param _bi The block (header) we wish to be mining. */ void setWork(BlockInfo const& _bi) { - WriteGuard l(x_work); - m_header = _bi; - m_work = PoW::package(m_header); - ReadGuard l(x_miners); - for (auto const& m: miners) + WorkPackage w; + { + WriteGuard l(x_work); + m_header = _bi; + w = m_work = PoW::package(m_header); + } + + ReadGuard l2(x_miners); + for (auto const& m: m_miners) m->setWork(m_work); } @@ -63,13 +72,13 @@ public: * @brief (Re)start miners for CPU only. * @returns true if started properly. */ - bool startCPU() { return start(); } + bool startCPU() { return start(); } /** * @brief (Re)start miners for GPU only. * @returns true if started properly. */ - bool startGPU() { start(); } + bool startGPU() { return start(); } /** * @brief Stop all mining activities. @@ -92,12 +101,14 @@ public: */ MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; } + using SolutionFound = std::function; + /** * @brief Provides a valid header based upon that received previously with setWork(). * @param _bi The now-valid header. * @return true if the header was good and that the Farm should pause until more work is submitted. */ - void onSolutionFound(function _handler) { m_onSolutionFound = _handler; } + void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } private: /** @@ -116,7 +127,7 @@ private: ReadGuard l(x_miners); for (std::shared_ptr const& m: m_miners) if (m.get() != _m) - m->pause(); + m->setWork(); m_work.headerHash = h256(); return true; } @@ -135,7 +146,7 @@ private: m_miners.clear(); m_miners.reserve(MinerType::instances()); for (unsigned i = 0; i < MinerType::instances(); ++i) - m_miners.push_back(new MinerType(std::make_pair(this, i))); + m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); return true; } @@ -149,7 +160,7 @@ private: WorkPackage m_work; BlockInfo m_header; - function m_onSolutionFound; + SolutionFound m_onSolutionFound; }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 0ab81728b..b72a29c00 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -183,7 +183,7 @@ public: virtual uint64_t hashrate() const = 0; /// Get hash of the current block to be mined minus the nonce (the 'work hash'). - virtual std::pair getWork() = 0; + virtual ProofOfWork::WorkPackage getWork() = 0; /// Submit the nonce for the proof-of-work. virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/Miner.h b/libethereum/Miner.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index e987e64cc..267961fd9 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -758,8 +758,9 @@ Json::Value WebThreeStubServerBase::eth_getWork() { Json::Value ret(Json::arrayValue); auto r = client()->getWork(); - ret.append(toJS(r.first)); - ret.append(toJS(r.second)); + ret.append(toJS(r.headerHash)); + ret.append(toJS(r.seedHash)); + ret.append(toJS(r.boundary)); return ret; } diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 8ea2133f0..bbe0d55ec 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -42,7 +42,7 @@ WebThreeDirect::WebThreeDirect( WithExisting _we, std::set const& _interfaces, NetworkPreferences const& _n, - bytesConstRef _network, int _miners + bytesConstRef _network ): m_clientVersion(_clientVersion), m_net(_clientVersion, _n, _network) @@ -50,7 +50,7 @@ WebThreeDirect::WebThreeDirect( if (_dbPath.size()) Defaults::setDBPath(_dbPath); if (_interfaces.count("eth")) - m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0, _miners)); + m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0)); if (_interfaces.count("shh")) m_whisper = m_net.registerCapability(new WhisperHost); diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index 87cf62d4a..90a4aa3c2 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -112,8 +112,7 @@ public: WithExisting _we = WithExisting::Trust, std::set const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(), - bytesConstRef _network = bytesConstRef(), - int _miners = -1 + bytesConstRef _network = bytesConstRef() ); /// Destructor. From 2693659e5656f388934c7e144714cf521f1f76ad Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 18:12:05 +0200 Subject: [PATCH 10/34] Various fixes for mining. --- alethzero/DownloadView.h | 1 - alethzero/MainWin.cpp | 10 ++-- alethzero/MiningView.cpp | 11 ++-- alethzero/MiningView.h | 4 +- eth/main.cpp | 17 +++--- exp/main.cpp | 54 +++++++++++++++---- libdevcore/Worker.h | 3 ++ libethcore/Ethash.cpp | 52 +++++++++--------- libethcore/Ethash.h | 22 +++++--- libethcore/Miner.h | 35 ++++++------ libethereum/Farm.h | 45 ++++++++++++---- mix/MixClient.cpp | 23 +++++--- mix/MixClient.h | 4 +- neth/main.cpp | 10 +--- test/TestHelper.cpp | 31 +++++++++++ test/TestHelper.h | 5 +- test/blockchain.cpp | 111 ++++++++++++++------------------------- test/dagger.cpp | 12 ++--- test/stateOriginal.cpp | 11 ++-- third/MainWin.cpp | 2 +- 20 files changed, 269 insertions(+), 194 deletions(-) diff --git a/alethzero/DownloadView.h b/alethzero/DownloadView.h index 22a11651c..d0fc445f8 100644 --- a/alethzero/DownloadView.h +++ b/alethzero/DownloadView.h @@ -32,7 +32,6 @@ #endif namespace dev { namespace eth { -struct MineInfo; class DownloadMan; }} diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 03d216a17..2e4478594 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -45,7 +45,7 @@ #endif #include #include -#include +#include #include #include #include @@ -164,7 +164,7 @@ Main::Main(QWidget *parent) : statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->blockCount); - ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(ProofOfWork::name()).arg(ProofOfWork::revision()).arg(dev::Version)); + ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version)); connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); @@ -952,7 +952,7 @@ void Main::on_preview_triggered() void Main::refreshMining() { - MineProgress p = ethereum()->miningProgress(); + MiningProgress p = ethereum()->miningProgress(); ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); if (!ui->miningView->isVisible()) return; @@ -1481,7 +1481,7 @@ void Main::on_blocks_currentItemChanged() s << "
Difficulty: " << info.difficulty << "" << "
"; if (info.number) { - auto e = Ethasher::eval(info); + auto e = EthashAux::eval(info); s << "
Proof-of-Work: " << e.value << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << " (mixhash: " << e.mixHash.abridged() << ")" << "
"; s << "
Parent: " << info.parentHash << "" << "
"; } @@ -1510,7 +1510,7 @@ void Main::on_blocks_currentItemChanged() s << line << "Nonce: " << uncle.nonce << "" << ""; s << line << "Hash w/o nonce: " << uncle.headerHash(WithoutNonce) << "" << ""; s << line << "Difficulty: " << uncle.difficulty << "" << ""; - auto e = Ethasher::eval(uncle); + auto e = EthashAux::eval(uncle); s << line << "Proof-of-Work: " << e.value << " <= " << (h256)u256((bigint(1) << 256) / uncle.difficulty) << " (mixhash: " << e.mixHash.abridged() << ")" << ""; } if (info.parentHash) diff --git a/alethzero/MiningView.cpp b/alethzero/MiningView.cpp index 63d1fcf99..e020408ea 100644 --- a/alethzero/MiningView.cpp +++ b/alethzero/MiningView.cpp @@ -36,7 +36,7 @@ using namespace dev::eth; // types using dev::eth::MineInfo; -using dev::eth::MineProgress; +using dev::eth::MiningProgress; // functions using dev::toString; @@ -50,12 +50,13 @@ MiningView::MiningView(QWidget* _p): QWidget(_p) { } -void MiningView::appendStats(list const& _i, MineProgress const& _p) +void MiningView::appendStats(list const& _i, MiningProgress const& _p) { + (void)_p; if (_i.empty()) return; - unsigned o = m_values.size(); +/* unsigned o = m_values.size(); for (MineInfo const& i: _i) { m_values.push_back(i.best); @@ -91,7 +92,7 @@ void MiningView::appendStats(list const& _i, MineProgress const& _p) m_completes.erase(remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}), m_completes.end()); m_progress = _p; - update(); + update();*/ } void MiningView::resetStats() @@ -101,6 +102,7 @@ void MiningView::resetStats() void MiningView::paintEvent(QPaintEvent*) { + /* Grapher g; QPainter p(this); @@ -115,4 +117,5 @@ void MiningView::paintEvent(QPaintEvent*) g.ruleY(r - 1, QColor(128, 128, 128)); for (auto r: m_completes) g.ruleY(r, QColor(192, 64, 64)); + */ } diff --git a/alethzero/MiningView.h b/alethzero/MiningView.h index 8f3135f75..65b9f2ec9 100644 --- a/alethzero/MiningView.h +++ b/alethzero/MiningView.h @@ -42,14 +42,14 @@ class MiningView: public QWidget public: MiningView(QWidget* _p = nullptr); - void appendStats(std::list const& _l, dev::eth::MineProgress const& _p); + void appendStats(std::list const& _l, dev::eth::MiningProgress const& _p); void resetStats(); protected: virtual void paintEvent(QPaintEvent*); private: - dev::eth::MineProgress m_progress; + dev::eth::MiningProgress m_progress; unsigned m_duration = 300; std::vector m_values; std::vector m_bests; diff --git a/eth/main.cpp b/eth/main.cpp index 05826dc0f..0ac53fab5 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -126,20 +126,22 @@ void help() #if ETH_JSONRPC << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl +#endif +#if ETH_EVMJIT + << " -J,--jit Enable EVM JIT (default: off)." << endl #endif << " -K,--kill First kill the blockchain." << endl << " --listen-ip Listen on the given port for incoming connections (default: 30303)." << endl << " -l,--listen Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl << " -u,--public-ip Force public ip to given (default: auto)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl - << " -n,--upnp Use upnp for NAT (default: on)." << endl + << " -n,-u,--upnp Use upnp for NAT (default: on)." << endl << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl << " -R,--rebuild First rebuild the blockchain from the existing database." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl - << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl << " -V,--version Show the version and exit." << endl @@ -337,7 +339,7 @@ int main(int argc, char** argv) exportFrom = argv[++i]; else if (arg == "--only" && i + 1 < argc) exportTo = exportFrom = argv[++i]; - else if ((arg == "-n" || arg == "--upnp") && i + 1 < argc) + else if ((arg == "-n" || arg == "-u" || arg == "--upnp") && i + 1 < argc) { string m = argv[++i]; if (isTrue(m)) @@ -489,15 +491,12 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "--jit") - { #if ETH_EVMJIT + else if (arg == "-J" || arg == "--jit") + { jit = true; -#else - cerr << "EVM JIT not enabled" << endl; - return -1; -#endif } +#endif else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") diff --git a/exp/main.cpp b/exp/main.cpp index 88f1075a9..933fda1a6 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -109,18 +109,50 @@ int main() #else int main() { -#if ETH_ETHASHCL - EthashCL ecl; + GenericFarm f; BlockInfo genesis = CanonBlockChain::genesis(); genesis.difficulty = 1 << 18; - cdebug << (h256)u256((bigint(1) << 256) / genesis.difficulty); - std::pair r; - while (!r.first.completed) - r = ecl.mine(genesis, 1000); - cdebug << r.second.mixHash << r.second.nonce; - EthashCL::assignResult(r.second, genesis); - assert(EthashCPU::verify(genesis)); -#endif + cdebug << genesis.boundary(); + + auto mine = [](GenericFarm& f, BlockInfo const& g, unsigned timeout) { + BlockInfo bi = g; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + ProofOfWork::assignResult(sol, bi); + return completed = true; + }); + f.setWork(bi); + for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush) + this_thread::sleep_for(chrono::milliseconds(100)); + cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad"); + }; + + f.startCPU(); + mine(f, genesis, 10); + mine(f, genesis, 10); + f.startGPU(); + + cdebug << "Good:"; + genesis.difficulty = 1 << 18; + genesis.noteDirty(); + mine(f, genesis, 3); + + cdebug << "Bad:"; + genesis.difficulty = (u256(1) << 40); + genesis.noteDirty(); + mine(f, genesis, 3); + + cdebug << "Good:"; + genesis.difficulty = 1 << 18; + genesis.noteDirty(); + mine(f, genesis, 3); + + cdebug << "Bad:"; + genesis.difficulty = (u256(1) << 40); + genesis.noteDirty(); + mine(f, genesis, 3); + return 0; } #endif diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 6a35d6c4c..24ff4cc15 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -66,6 +66,9 @@ protected: /// Called when is to be stopped, just prior to thread being joined. virtual void doneWorking() {} + /// Blocks caller into worker thread has finished. + void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} } + private: std::string m_name; unsigned m_idleWaitMs = 0; diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 03c7a3654..f66188976 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -63,7 +63,7 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) { WorkPackage ret; ret.boundary = _bi.boundary(); - ret.headerHash = _bi.headerHash(WithNonce); + ret.headerHash = _bi.headerHash(WithoutNonce); ret.seedHash = _bi.seedHash(); return ret; } @@ -95,9 +95,8 @@ bool Ethash::verify(BlockInfo const& _header) return false; #endif - h256 boundary = u256((bigint(1) << 256) / _header.difficulty); auto result = EthashAux::eval(_header); - bool slow = result.value <= boundary && result.mixHash == _header.mixHash; + bool slow = result.value <= _header.boundary() && result.mixHash == _header.mixHash; #if ETH_DEBUG || !ETH_TRUE if (!pre && slow) @@ -107,7 +106,7 @@ bool Ethash::verify(BlockInfo const& _header) cwarn << "nonce:" << _header.nonce; cwarn << "mixHash:" << _header.mixHash; cwarn << "difficulty:" << _header.difficulty; - cwarn << "boundary:" << boundary; + cwarn << "boundary:" << _header.boundary(); cwarn << "result.value:" << result.value; cwarn << "result.mixHash:" << result.mixHash; } @@ -125,16 +124,18 @@ void Ethash::CPUMiner::workLoop() ethash_return_value ethashReturn; auto p = EthashAux::params(m_work.seedHash); - void const* dagPointer = EthashAux::full(m_work.headerHash).data(); + void const* dagPointer = EthashAux::full(m_work.seedHash).data(); uint8_t const* headerHashPointer = m_work.headerHash.data(); h256 boundary = m_work.boundary; - unsigned hashCount = 0; + unsigned hashCount = 1; for (; !shouldStop(); tryNonce++, hashCount++) { ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) break; + if (!(hashCount % 1000)) + accumulateHashes(1000); } } @@ -148,6 +149,7 @@ public: void abort() { Guard l(x_all); + m_owner->m_work.headerHash = h256(); if (m_aborted) return; // cdebug << "Attempting to abort"; @@ -159,8 +161,6 @@ public: m_aborted = m_abort = false; } - uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } - protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { @@ -180,7 +180,7 @@ protected: { Guard l(x_all); // cdebug << "Searched" << _count << "from" << _startNonce; - m_total += _count; + m_owner->accumulateHashes(_count); m_last = _startNonce + _count; if (m_abort) { @@ -192,7 +192,6 @@ protected: private: Mutex x_all; - uint64_t m_total; uint64_t m_last; bool m_abort = false; bool m_aborted = true; @@ -214,38 +213,43 @@ Ethash::GPUMiner::~GPUMiner() bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; - Result r = EthashAux::eval(m_lastWork.seedHash, m_lastWork.headerHash, n); - if (r.value < m_lastWork.boundary) + Result r = EthashAux::eval(m_work.seedHash, m_work.headerHash, n); + if (r.value < m_work.boundary) return submitProof(Solution{n, r.mixHash}); return false; } void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { - if (!m_miner || m_minerSeed != _work.seedHash) + m_work = _work; + startWorking(); +} + +void Ethash::GPUMiner::workLoop() +{ + // take local copy of work since it may end up being overwritten by kickOff/pause. + WorkPackage w = m_work; + if (!m_miner || m_minerSeed != w.seedHash) { - if (m_miner) - m_hook->abort(); + m_minerSeed = w.seedHash; delete m_miner; m_miner = new ethash_cl_miner; - auto p = EthashAux::params(_work.seedHash); - auto cb = [&](void* d) { EthashAux::full(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + auto p = EthashAux::params(m_minerSeed); + auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); } - if (m_lastWork.headerHash != _work.headerHash) - { - m_hook->abort(); - uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); - m_miner->search(_work.headerHash.data(), upper64OfBoundary, *m_hook); - } - m_lastWork = _work; + + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); + m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook); } void Ethash::GPUMiner::pause() { m_hook->abort(); + stopWorking(); + m_work.headerHash = h256(); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 1a7d82149..8f1ba3eb3 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -59,6 +59,11 @@ public: struct WorkPackage { + WorkPackage() = default; + + void reset() { headerHash = h256(); } + operator bool() const { return headerHash != h256(); } + h256 boundary; h256 headerHash; ///< When h256() means "pause until notified a new work package is available". h256 seedHash; @@ -89,17 +94,16 @@ public: startWorking(); } - void pause() override { stopWorking(); } + void pause() override { stopWorking(); m_work.reset(); } private: void workLoop() override; WorkPackage m_work; - MineInfo m_info; }; #if ETH_ETHASHCL || !ETH_TRUE - class GPUMiner: public Miner + class GPUMiner: public Miner, Worker { friend class dev::eth::EthashCLHook; @@ -114,14 +118,16 @@ public: void pause() override; private: + void workLoop() override; bool report(uint64_t _nonce); - EthashCLHook* m_hook; - ethash_cl_miner* m_miner; + using Miner::accumulateHashes; + + EthashCLHook* m_hook = nullptr; + ethash_cl_miner* m_miner = nullptr; - h256 m_minerSeed; - WorkPackage m_lastWork; ///< Work loaded into m_miner. - MineInfo m_info; + h256 m_minerSeed; ///< Last seed in m_miner + WorkPackage m_work; ///< Work to be done by GPU, set with kickOff and picked up in workLoop. }; #else using GPUMiner = CPUMiner; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index 9372c06b1..ea51b0eb5 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -34,30 +34,24 @@ namespace dev namespace eth { -struct MineInfo -{ - MineInfo() = default; - MineInfo(bool _completed): completed(_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; - unsigned hashes = 0; - bool completed = false; -}; - /** * @brief Describes the progress of a mining operation. */ struct MiningProgress { - void combine(MiningProgress 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. +// MiningProgress& operator+=(MiningProgress const& _mp) { hashes += _mp.hashes; ms = std::max(ms, _mp.ms); return *this; } unsigned hashes = 0; ///< Total number of hashes computed. unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; +struct MineInfo: public MiningProgress {}; + +inline std::ostream& operator<<(std::ostream& _out, MiningProgress const& _p) +{ + _out << (_p.hashes * 1000 / _p.ms) << "H/s = " << _p.hashes << " hashes / " << (double(_p.ms) / 1000) << "s"; + return _out; +} + template class GenericMiner; /** @@ -103,13 +97,18 @@ public: void setWork(WorkPackage const& _work = WorkPackage()) { Guard l(x_work); + if (_work.headerHash == m_work.headerHash) + return; if (_work.headerHash != h256()) - kickOff(m_work); + kickOff(_work); else if (m_work.headerHash == h256() && _work.headerHash != h256()) pause(); m_work = _work; + m_hashCount = 0; } + unsigned hashCount() { return m_hashCount; } + unsigned index() const { return m_index; } protected: @@ -146,12 +145,16 @@ protected: WorkPackage const& work() const { return m_work; } + void accumulateHashes(unsigned _n) { m_hashCount += _n; } + private: FarmFace* m_farm = nullptr; unsigned m_index; Mutex x_work; WorkPackage m_work; + + unsigned m_hashCount = 0; }; } diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 09c7f0e78..c20c27e6d 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -56,16 +56,16 @@ public: */ void setWork(BlockInfo const& _bi) { - WorkPackage w; - { - WriteGuard l(x_work); - m_header = _bi; - w = m_work = PoW::package(m_header); - } - + WriteGuard l(x_work); ReadGuard l2(x_miners); + m_header = _bi; + auto p = PoW::package(m_header); + if (p.headerHash == m_work.headerHash) + return; + m_work = p; for (auto const& m: m_miners) m->setWork(m_work); + resetTimer(); } /** @@ -99,7 +99,19 @@ public: * @brief Get information on the progress of mining this work package. * @return The progress with mining so far. */ - MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; } + MiningProgress const& miningProgress() const + { + MiningProgress p; + p.ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_lastStart).count(); + { + ReadGuard l2(x_miners); + for (auto const& i: m_miners) + p.hashes += i->hashCount(); + } + ReadGuard l(x_progress); + m_progress = p; + return m_progress; + } using SolutionFound = std::function; @@ -110,6 +122,8 @@ public: */ void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } + WorkPackage work() const { ReadGuard l(x_work); return m_work; } + private: /** * @brief Called from a Miner to note a WorkPackage has a solution. @@ -140,21 +154,32 @@ private: template bool start() { - WriteGuard l(x_miners); + ReadGuard l(x_work); + WriteGuard l2(x_miners); if (!m_miners.empty() && !!std::dynamic_pointer_cast(m_miners[0])) return true; m_miners.clear(); m_miners.reserve(MinerType::instances()); for (unsigned i = 0; i < MinerType::instances(); ++i) + { m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); + m_miners.back()->setWork(m_work); + } + resetTimer(); return true; } + void resetTimer() + { + m_lastStart = std::chrono::steady_clock::now(); + } + mutable SharedMutex x_miners; std::vector> m_miners; mutable SharedMutex x_progress; - MiningProgress m_progress; + mutable MiningProgress m_progress; + std::chrono::steady_clock::time_point m_lastStart; mutable SharedMutex x_work; WorkPackage m_work; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 254ceb325..f9513e80c 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -20,6 +20,7 @@ * Ethereum IDE client. */ +#include "MixClient.h" #include #include #include @@ -28,10 +29,8 @@ #include #include #include - #include "Exceptions.h" -#include "MixClient.h" - +using namespace std; using namespace dev; using namespace dev::eth; @@ -250,9 +249,17 @@ void MixClient::mine() { WriteGuard l(x_state); m_state.commitToMine(bc()); - ProofOfWork pow; - while (!m_state.mine(&pow).completed) {} - m_state.completeMine(); + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + return completed = m_state.completeMine(sol); + }); + f.setWork(m_state.info()); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); + bc().import(m_state.blockData(), m_stateDB); m_state.sync(bc()); m_startState = m_state; @@ -392,9 +399,9 @@ uint64_t MixClient::hashrate() const return 0; } -eth::MineProgress MixClient::miningProgress() const +eth::MiningProgress MixClient::miningProgress() const { - return eth::MineProgress(); + return eth::MiningProgress(); } } diff --git a/mix/MixClient.h b/mix/MixClient.h index 0d1cce2bc..81087f3b4 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -67,8 +67,8 @@ public: void stopMining() override; bool isMining() const override; uint64_t hashrate() const override; - eth::MineProgress miningProgress() const override; - std::pair getWork() override { return std::pair(); } + eth::MiningProgress miningProgress() const override; + eth::ProofOfWork::WorkPackage getWork() override { return eth::ProofOfWork::WorkPackage(); } bool submitWork(eth::ProofOfWork::Solution const&) override { return false; } virtual void flushTransactions() override {} diff --git a/neth/main.cpp b/neth/main.cpp index fd3d3f403..4c38da0a4 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -40,7 +40,6 @@ #include #include #endif -#include #include "BuildInfo.h" #undef KEY_EVENT // from windows.h @@ -332,7 +331,6 @@ int main(int argc, char** argv) unsigned mining = ~(unsigned)0; NodeMode mode = NodeMode::Full; unsigned peers = 5; - int miners = -1; #if ETH_JSONRPC int jsonrpc = 8080; #endif @@ -502,8 +500,6 @@ int main(int argc, char** argv) g_logVerbosity = atoi(argv[++i]); else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) peers = atoi(argv[++i]); - else if ((arg == "-t" || arg == "--miners") && i + 1 < argc) - miners = atoi(argv[++i]); else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) { string m = argv[++i]; @@ -553,9 +549,7 @@ int main(int argc, char** argv) killChain ? WithExisting::Kill : WithExisting::Trust, mode == NodeMode::Full ? set{"eth", "shh"} : set(), netPrefs, - &nodesState, - miners - ); + &nodesState); web3.setIdealPeerCount(peers); std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); @@ -1253,7 +1247,7 @@ int main(int argc, char** argv) if (c && c->isMining()) { mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON"); - dev::eth::MineProgress p = c->miningProgress(); + dev::eth::MiningProgress p = c->miningProgress(); auto speed = boost::format("%2% kH/s @ %1%s") % (p.ms / 1000) % (p.ms ? p.hashes / p.ms : 0); mvwprintw(consolewin, qheight - 2, width / 4 - speed.str().length() - 2, speed.str().c_str()); } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index e86b84aad..8b528b3ee 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -62,6 +62,37 @@ void connectClients(Client& c1, Client& c2) c2.connect("127.0.0.1", c1Port); #endif } + +void mine(State& s, BlockChain const& _bc) +{ + s.commitToMine(_bc); + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + return completed = s.completeMine(sol); + }); + f.setWork(s.info()); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); +} + +void mine(BlockInfo& _bi) +{ + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + ProofOfWork::assignResult(sol, _bi); + return completed = true; + }); + f.setWork(_bi); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); +} + } namespace test diff --git a/test/TestHelper.h b/test/TestHelper.h index 04ca95be4..92745bc36 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -36,9 +36,12 @@ namespace eth { class Client; +class State; void mine(Client& c, int numBlocks); void connectClients(Client& c1, Client& c2); +void mine(State& _s, BlockChain const& _bc); +void mine(BlockInfo& _bi); } @@ -225,7 +228,5 @@ public: }; }; - - } } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 6d7bc97ef..258117695 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -190,11 +190,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { state.sync(bc); state.sync(bc, txs, gp); - state.commitToMine(bc); - MineInfo info; - ProofOfWork pow; - for (info.completed = false; !info.completed; info = state.mine(&pow)) {} - state.completeMine(); + mine(state, bc); } catch (Exception const& _e) { @@ -519,76 +515,55 @@ bytes createBlockRLPFromFields(mObject& _tObj) return rlpStream.out(); } -void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj) +void overwriteBlockHeader(BlockInfo& _header, mObject& _blObj) { - if (_blObj["blockHeader"].get_obj().size() != 14) + auto ho = _blObj["blockHeader"].get_obj(); + if (ho.size() != 14) { - - BlockInfo tmp = _currentBlockHeader; - - if (_blObj["blockHeader"].get_obj().count("parentHash")) - tmp.parentHash = h256(_blObj["blockHeader"].get_obj()["parentHash"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("uncleHash")) - tmp.sha3Uncles = h256(_blObj["blockHeader"].get_obj()["uncleHash"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("coinbase")) - tmp.coinbaseAddress = Address(_blObj["blockHeader"].get_obj()["coinbase"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("stateRoot")) - tmp.stateRoot = h256(_blObj["blockHeader"].get_obj()["stateRoot"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("transactionsTrie")) - tmp.transactionsRoot = h256(_blObj["blockHeader"].get_obj()["transactionsTrie"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("receiptTrie")) - tmp.receiptsRoot = h256(_blObj["blockHeader"].get_obj()["receiptTrie"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("bloom")) - tmp.logBloom = LogBloom(_blObj["blockHeader"].get_obj()["bloom"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("difficulty")) - tmp.difficulty = toInt(_blObj["blockHeader"].get_obj()["difficulty"]); - - if (_blObj["blockHeader"].get_obj().count("number")) - tmp.number = toInt(_blObj["blockHeader"].get_obj()["number"]); - - if (_blObj["blockHeader"].get_obj().count("gasLimit")) - tmp.gasLimit = toInt(_blObj["blockHeader"].get_obj()["gasLimit"]); - - if (_blObj["blockHeader"].get_obj().count("gasUsed")) - tmp.gasUsed = toInt(_blObj["blockHeader"].get_obj()["gasUsed"]); - - if (_blObj["blockHeader"].get_obj().count("timestamp")) - tmp.timestamp = toInt(_blObj["blockHeader"].get_obj()["timestamp"]); - - if (_blObj["blockHeader"].get_obj().count("extraData")) - tmp.extraData = importByteArray(_blObj["blockHeader"].get_obj()["extraData"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("mixHash")) - tmp.mixHash = h256(_blObj["blockHeader"].get_obj()["mixHash"].get_str()); + BlockInfo tmp = _header; + if (ho.count("parentHash")) + tmp.parentHash = h256(ho["parentHash"].get_str()); + if (ho.count("uncleHash")) + tmp.sha3Uncles = h256(ho["uncleHash"].get_str()); + if (ho.count("coinbase")) + tmp.coinbaseAddress = Address(ho["coinbase"].get_str()); + if (ho.count("stateRoot")) + tmp.stateRoot = h256(ho["stateRoot"].get_str()); + if (ho.count("transactionsTrie")) + tmp.transactionsRoot = h256(ho["transactionsTrie"].get_str()); + if (ho.count("receiptTrie")) + tmp.receiptsRoot = h256(ho["receiptTrie"].get_str()); + if (ho.count("bloom")) + tmp.logBloom = LogBloom(ho["bloom"].get_str()); + if (ho.count("difficulty")) + tmp.difficulty = toInt(ho["difficulty"]); + if (ho.count("number")) + tmp.number = toInt(ho["number"]); + if (ho.count("gasLimit")) + tmp.gasLimit = toInt(ho["gasLimit"]); + if (ho.count("gasUsed")) + tmp.gasUsed = toInt(ho["gasUsed"]); + if (ho.count("timestamp")) + tmp.timestamp = toInt(ho["timestamp"]); + if (ho.count("extraData")) + tmp.extraData = importByteArray(ho["extraData"].get_str()); + if (ho.count("mixHash")) + tmp.mixHash = h256(ho["mixHash"].get_str()); + tmp.noteDirty(); // find new valid nonce - - if (tmp != _currentBlockHeader) + if (tmp != _header) { - _currentBlockHeader = tmp; - - ProofOfWork pow; - std::pair ret; - while (!ProofOfWork::verify(_currentBlockHeader)) - { - ret = pow.mine(_currentBlockHeader, 1000, true); - Ethash::assignResult(ret.second, _currentBlockHeader); - } + mine(tmp); + _header = tmp; } } else { // take the blockheader as is - const bytes c_blockRLP = createBlockRLPFromFields(_blObj["blockHeader"].get_obj()); + const bytes c_blockRLP = createBlockRLPFromFields(ho); const RLP c_bRLP(c_blockRLP); - _currentBlockHeader.populateFromHeader(c_bRLP, IgnoreNonce); + _header.populateFromHeader(c_bRLP, IgnoreNonce); } } @@ -620,13 +595,7 @@ BlockInfo constructBlock(mObject& _o) void updatePoW(BlockInfo& _bi) { - ProofOfWork pow; - std::pair ret; - while (!ProofOfWork::verify(_bi)) - { - ret = pow.mine(_bi, 10000, true); - Ethash::assignResult(ret.second, _bi); - } + mine(_bi); _bi.noteDirty(); } diff --git a/test/dagger.cpp b/test/dagger.cpp index 4abba5090..367c422ad 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -25,7 +25,7 @@ #include "JsonSpiritHeaders.h" #include #include -#include +#include #include #include "TestHelper.h" @@ -63,18 +63,18 @@ BOOST_AUTO_TEST_CASE(basic_test) unsigned cacheSize(o["cache_size"].get_int()); h256 cacheHash(o["cache_hash"].get_str()); - BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize); - BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->light(header), cacheSize)), cacheHash); + BOOST_REQUIRE_EQUAL(EthashAux::get()->params(header).cache_size, cacheSize); + BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)EthashAux::get()->light(header), cacheSize)), cacheHash); #if TEST_FULL unsigned fullSize(o["full_size"].get_int()); h256 fullHash(o["full_hash"].get_str()); - BOOST_REQUIRE_EQUAL(Ethasher::get()->full(header).size(), fullSize); - BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->full(header)), fullHash); + BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header).size(), fullSize); + BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)), fullHash); #endif h256 result(o["result"].get_str()); - Ethasher::Result r = Ethasher::eval(header); + Ethash::Result r = EthashAux::eval(header); BOOST_REQUIRE_EQUAL(r.value, result); BOOST_REQUIRE_EQUAL(r.mixHash, header.mixHash); } diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 40f759434..e1a3c7c4a 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -25,7 +25,9 @@ #include #include #include +#include #include +#include "TestHelper.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -67,10 +69,8 @@ BOOST_AUTO_TEST_CASE(Complex) cout << s; // Mine to get some ether! - s.commitToMine(bc); - ProofOfWork pow; - while (!s.mine(&pow).completed) {} - s.completeMine(); + mine(s, bc); + bc.attemptImport(s.blockData(), stateDB); cout << bc; @@ -89,8 +89,7 @@ BOOST_AUTO_TEST_CASE(Complex) // Mine to get some ether and set in stone. s.commitToMine(bc); s.commitToMine(bc); - while (!s.mine(&pow).completed) {} - s.completeMine(); + mine(s, bc); bc.attemptImport(s.blockData(), stateDB); cout << bc; diff --git a/third/MainWin.cpp b/third/MainWin.cpp index b03723131..12625ffbc 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -465,7 +465,7 @@ void Main::on_urlEdit_returnPressed() void Main::refreshMining() { - dev::eth::MineProgress p = ethereum()->miningProgress(); + dev::eth::MiningProgress p = ethereum()->miningProgress(); ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); } From 54e40484c1b9bf2ffcd2a0d08426ebff313d6b3f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 21:36:55 +0200 Subject: [PATCH 11/34] Thread management fixes for Farm. --- exp/main.cpp | 21 +++++++++------------ libdevcore/Worker.cpp | 22 +++++++++++++++++----- libdevcore/Worker.h | 10 +++++++++- libethcore/Ethash.cpp | 36 +++++++++++++++++++++--------------- libethcore/Ethash.h | 10 +++------- libethcore/Miner.h | 31 ++++++++++++++++++------------- libethereum/Farm.h | 7 ++++++- 7 files changed, 83 insertions(+), 54 deletions(-) diff --git a/exp/main.cpp b/exp/main.cpp index 933fda1a6..2055c49e1 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -125,33 +125,30 @@ int main() f.setWork(bi); for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush) this_thread::sleep_for(chrono::milliseconds(100)); + cout << endl << flush; cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad"); }; + Ethash::prep(genesis); + + genesis.difficulty = u256(1) << 40; + genesis.noteDirty(); f.startCPU(); mine(f, genesis, 10); - mine(f, genesis, 10); + f.startGPU(); cdebug << "Good:"; genesis.difficulty = 1 << 18; genesis.noteDirty(); - mine(f, genesis, 3); + mine(f, genesis, 30); cdebug << "Bad:"; genesis.difficulty = (u256(1) << 40); genesis.noteDirty(); - mine(f, genesis, 3); + mine(f, genesis, 30); - cdebug << "Good:"; - genesis.difficulty = 1 << 18; - genesis.noteDirty(); - mine(f, genesis, 3); - - cdebug << "Bad:"; - genesis.difficulty = (u256(1) << 40); - genesis.noteDirty(); - mine(f, genesis, 3); + f.stop(); return 0; } diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index bc8fe97f2..65e8efcbb 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -27,12 +27,20 @@ using namespace std; using namespace dev; -void Worker::startWorking() +void Worker::startWorking(IfRunning _ir) { cnote << "startWorking for thread" << m_name; Guard l(x_work); - if (m_work) - return; + + if (m_work && m_work->joinable()) + try { + if (_ir == IfRunning::Detach) + m_work->detach(); + else if (_ir == IfRunning::Join) + m_work->join(); + else + return; + } catch (...) {} cnote << "Spawning" << m_name; m_stop = false; m_work.reset(new thread([&]() @@ -40,6 +48,7 @@ void Worker::startWorking() setThreadName(m_name.c_str()); startedWorking(); workLoop(); + m_work->detach(); cnote << "Finishing up worker thread"; doneWorking(); })); @@ -49,11 +58,14 @@ void Worker::stopWorking() { cnote << "stopWorking for thread" << m_name; Guard l(x_work); - if (!m_work) + if (!m_work || !m_work->joinable()) return; cnote << "Stopping" << m_name; m_stop = true; - m_work->join(); + try { + m_work->join(); + } + catch (...) {} m_work.reset(); cnote << "Stopped" << m_name; } diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 24ff4cc15..287ff6d6f 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -23,11 +23,19 @@ #include #include +#include #include "Guards.h" namespace dev { +enum class IfRunning +{ + Fail, + Join, + Detach +}; + class Worker { protected: @@ -45,7 +53,7 @@ protected: void setName(std::string _n) { if (!isWorking()) m_name = _n; } /// Starts worker thread; causes startedWorking() to be called. - void startWorking(); + void startWorking(IfRunning _ir = IfRunning::Fail); /// Stop worker thread; causes call to stopWorking(). void stopWorking(); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index f66188976..3dd9d3b60 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -30,9 +30,10 @@ #include #include #include +#include +#include #include #include -#include #include #if ETH_ETHASHCL || !ETH_TRUE #include @@ -123,10 +124,12 @@ void Ethash::CPUMiner::workLoop() uint64_t tryNonce = (uint64_t)(u64)Nonce::random(s_eng); ethash_return_value ethashReturn; - auto p = EthashAux::params(m_work.seedHash); - void const* dagPointer = EthashAux::full(m_work.seedHash).data(); - uint8_t const* headerHashPointer = m_work.headerHash.data(); - h256 boundary = m_work.boundary; + WorkPackage w = work(); + + auto p = EthashAux::params(w.seedHash); + void const* dagPointer = EthashAux::full(w.seedHash).data(); + uint8_t const* headerHashPointer = w.headerHash.data(); + h256 boundary = w.boundary; unsigned hashCount = 1; for (; !shouldStop(); tryNonce++, hashCount++) { @@ -149,7 +152,6 @@ public: void abort() { Guard l(x_all); - m_owner->m_work.headerHash = h256(); if (m_aborted) return; // cdebug << "Attempting to abort"; @@ -158,13 +160,17 @@ public: std::this_thread::sleep_for(chrono::milliseconds(30)); // if (!m_aborted) // cwarn << "Couldn't abort. Abandoning OpenCL process."; + } + + void reset() + { m_aborted = m_abort = false; } protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { -// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); +// dev::operator <<(std::cerr << "Found nonces: ", vector(_nonces, _nonces + _count)) << std::endl; for (uint32_t i = 0; i < _count; ++i) { if (m_owner->report(_nonces[i])) @@ -179,7 +185,7 @@ protected: virtual bool searched(uint64_t _startNonce, uint32_t _count) override { Guard l(x_all); -// cdebug << "Searched" << _count << "from" << _startNonce; +// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl; m_owner->accumulateHashes(_count); m_last = _startNonce + _count; if (m_abort) @@ -206,29 +212,30 @@ Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Ethash::GPUMiner::~GPUMiner() { - delete m_hook; + pause(); delete m_miner; + delete m_hook; } bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; - Result r = EthashAux::eval(m_work.seedHash, m_work.headerHash, n); - if (r.value < m_work.boundary) + Result r = EthashAux::eval(work().seedHash, work().headerHash, n); + if (r.value < work().boundary) return submitProof(Solution{n, r.mixHash}); return false; } -void Ethash::GPUMiner::kickOff(WorkPackage const& _work) +void Ethash::GPUMiner::kickOff() { - m_work = _work; + m_hook->reset(); startWorking(); } void Ethash::GPUMiner::workLoop() { // take local copy of work since it may end up being overwritten by kickOff/pause. - WorkPackage w = m_work; + WorkPackage w = work(); if (!m_miner || m_minerSeed != w.seedHash) { m_minerSeed = w.seedHash; @@ -249,7 +256,6 @@ void Ethash::GPUMiner::pause() { m_hook->abort(); stopWorking(); - m_work.headerHash = h256(); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 8f1ba3eb3..06e6b9e96 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -87,19 +87,16 @@ public: static unsigned instances() { return std::thread::hardware_concurrency(); } protected: - void kickOff(WorkPackage const& _work) override + void kickOff() override { stopWorking(); - m_work = _work; startWorking(); } - void pause() override { stopWorking(); m_work.reset(); } + void pause() override { stopWorking(); } private: void workLoop() override; - - WorkPackage m_work; }; #if ETH_ETHASHCL || !ETH_TRUE @@ -114,7 +111,7 @@ public: static unsigned instances() { return 1; } protected: - void kickOff(WorkPackage const& _work) override; + void kickOff() override; void pause() override; private: @@ -127,7 +124,6 @@ public: ethash_cl_miner* m_miner = nullptr; h256 m_minerSeed; ///< Last seed in m_miner - WorkPackage m_work; ///< Work to be done by GPU, set with kickOff and picked up in workLoop. }; #else using GPUMiner = CPUMiner; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index ea51b0eb5..084cc59ff 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -40,13 +40,13 @@ namespace eth struct MiningProgress { // MiningProgress& operator+=(MiningProgress const& _mp) { hashes += _mp.hashes; ms = std::max(ms, _mp.ms); return *this; } - unsigned hashes = 0; ///< Total number of hashes computed. - unsigned ms = 0; ///< Total number of milliseconds of mining thus far. + uint64_t hashes = 0; ///< Total number of hashes computed. + uint64_t ms = 0; ///< Total number of milliseconds of mining thus far. }; struct MineInfo: public MiningProgress {}; -inline std::ostream& operator<<(std::ostream& _out, MiningProgress const& _p) +inline std::ostream& operator<<(std::ostream& _out, MiningProgress _p) { _out << (_p.hashes * 1000 / _p.ms) << "H/s = " << _p.hashes << " hashes / " << (double(_p.ms) / 1000) << "s"; return _out; @@ -97,17 +97,19 @@ public: void setWork(WorkPackage const& _work = WorkPackage()) { Guard l(x_work); - if (_work.headerHash == m_work.headerHash) - return; - if (_work.headerHash != h256()) - kickOff(_work); - else if (m_work.headerHash == h256() && _work.headerHash != h256()) - pause(); + auto old = m_work; m_work = _work; + if (!!m_work) + { + pause(); + kickOff(); + } + else if (!m_work && !!old) + pause(); m_hashCount = 0; } - unsigned hashCount() { return m_hashCount; } + uint64_t hashCount() { return m_hashCount; } unsigned index() const { return m_index; } @@ -119,7 +121,7 @@ protected: * @brief Begin working on a given work package, discarding any previous work. * @param _work The package for which to find a solution. */ - virtual void kickOff(WorkPackage const& _work) = 0; + virtual void kickOff() = 0; /** * @brief No work left to be done. Pause until told to kickOff(). @@ -138,7 +140,10 @@ protected: if (m_farm) { Guard l(x_work); - return m_farm->submitProof(_s, m_work, this); + if (!m_farm->submitProof(_s, m_work, this)) + return false; + m_work.reset(); + return true; } return true; } @@ -154,7 +159,7 @@ private: Mutex x_work; WorkPackage m_work; - unsigned m_hashCount = 0; + uint64_t m_hashCount = 0; }; } diff --git a/libethereum/Farm.h b/libethereum/Farm.h index c20c27e6d..6b65e37c2 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -50,6 +50,11 @@ public: using Solution = typename PoW::Solution; using Miner = GenericMiner; + ~GenericFarm() + { + stop(); + } + /** * @brief Sets the current mining mission. * @param _bi The block (header) we wish to be mining. @@ -142,7 +147,7 @@ private: for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); - m_work.headerHash = h256(); + m_work.reset(); return true; } return false; From e195a254dae62a064a82807d450c2553a47b830e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 22:49:17 +0200 Subject: [PATCH 12/34] Fix CPU mining deadlock bug. --- exp/main.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++- libethcore/Miner.h | 6 ++-- libethereum/Farm.h | 4 ++- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/exp/main.cpp b/exp/main.cpp index 2055c49e1..54b75d0e2 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -25,6 +25,7 @@ #include "libethash-cl/cl.hpp" #endif #include +#include #include #include #include @@ -106,7 +107,7 @@ int main() cnote << "State after transaction: " << s; cnote << before.diff(s); } -#else +#elif 0 int main() { GenericFarm f; @@ -152,5 +153,76 @@ int main() return 0; } +#else + +void mine(State& s, BlockChain const& _bc) +{ + s.commitToMine(_bc); + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + return completed = s.completeMine(sol); + }); + f.setWork(s.info()); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); +} + +int main() +{ + cnote << "Testing State..."; + + KeyPair me = sha3("Gav Wood"); + KeyPair myMiner = sha3("Gav's Miner"); +// KeyPair you = sha3("123"); + + Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count())); + + OverlayDB stateDB = State::openDB(); + CanonBlockChain bc; + cout << bc; + + State s(stateDB, BaseState::CanonGenesis, myMiner.address()); + cout << s; + + // Sync up - this won't do much until we use the last state. + s.sync(bc); + + cout << s; + + // Mine to get some ether! + mine(s, bc); + + bc.attemptImport(s.blockData(), stateDB); + + cout << bc; + + s.sync(bc); + + cout << s; + + // Inject a transaction to transfer funds from miner to me. + Transaction t(1000, 10000, 30000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); + assert(t.sender() == myMiner.address()); + s.execute(bc.lastHashes(), t); + + cout << s; + + // Mine to get some ether and set in stone. + s.commitToMine(bc); + s.commitToMine(bc); + mine(s, bc); + bc.attemptImport(s.blockData(), stateDB); + + cout << bc; + + s.sync(bc); + + cout << s; + + return 0; +} #endif diff --git a/libethcore/Miner.h b/libethcore/Miner.h index 084cc59ff..a048b0238 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -69,11 +69,11 @@ public: /** * @brief Called from a Miner to note a WorkPackage has a solution. * @param _p The solution. - * @param _wp The WorkPackage that the Solution is for. + * @param _wp The WorkPackage that the Solution is for; this will be reset if the work is accepted. * @param _finder The miner that found it. * @return true iff the solution was good (implying that mining should be . */ - virtual bool submitProof(Solution const& _p, WorkPackage const& _wp, Miner* _finder) = 0; + virtual bool submitProof(Solution const& _p, WorkPackage& io_wp, Miner* _finder) = 0; }; /** @@ -139,10 +139,8 @@ protected: { if (m_farm) { - Guard l(x_work); if (!m_farm->submitProof(_s, m_work, this)) return false; - m_work.reset(); return true; } return true; diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 6b65e37c2..56bbcb9df 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -136,8 +136,9 @@ private: * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ - bool submitProof(Solution const& _s, WorkPackage const& _wp, Miner* _m) override + bool submitProof(Solution const& _s, WorkPackage& _wp, Miner* _m) override { + ReadGuard l(x_work); if (_wp.headerHash != m_work.headerHash) return false; @@ -147,6 +148,7 @@ private: for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); + _wp.reset(); m_work.reset(); return true; } From 7c3a920e1d1c7ea8ac58d8d27430f391d1b48878 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 09:39:00 +0200 Subject: [PATCH 13/34] Various threading fixes. --- CMakeLists.txt | 9 +++++++++ alethzero/CMakeLists.txt | 2 +- alethzero/Transact.cpp | 4 ++-- exp/main.cpp | 28 ++++++++++++++++++++++++++-- libdevcore/Worker.cpp | 4 ++-- libethcore/Miner.h | 31 ++++++++++++++++++------------- libethereum/Client.cpp | 34 ++++++++++++++++++++++++++++++---- libethereum/Client.h | 9 ++++++--- libethereum/Farm.h | 33 ++++++++++++--------------------- 9 files changed, 106 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6a906b59..25b7dcb50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,6 +244,15 @@ elseif (BUNDLE STREQUAL "full") set(TOOLS ON) set(TESTS ON) set(FATDB ON) +elseif (BUNDLE STREQUAL "core") + set(SERPENT OFF) + set(SOLIDITY ON) + set(USENPM OFF) + set(GUI ON) + set(NCURSES OFF) + set(TOOLS ON) + set(TESTS OFF) + set(FATDB ON) elseif (BUNDLE STREQUAL "tests") set(SERPENT ${DECENT_PLATFORM}) set(SOLIDITY ON) diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index c81c86222..b60bca425 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -59,7 +59,7 @@ target_link_libraries(${EXECUTABLE} jsqrc) target_link_libraries(${EXECUTABLE} natspec) target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) +if (SERPENT) target_link_libraries(${EXECUTABLE} serpent) endif() diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index f1f7477fe..1ebdf9e23 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -37,7 +37,7 @@ #include #include #include -#ifndef _MSC_VER +#if ETH_SERPENT #include #include #endif @@ -220,7 +220,7 @@ static tuple, bytes, string> userInputToCode(string const& _user, errors.push_back("Solidity: Uncaught exception"); } } -#ifndef _MSC_VER +#if ETH_SERPENT else if (sourceIsSerpent(_user)) { try diff --git a/exp/main.cpp b/exp/main.cpp index 54b75d0e2..20f287f43 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -153,7 +154,7 @@ int main() return 0; } -#else +#elif 0 void mine(State& s, BlockChain const& _bc) { @@ -169,7 +170,7 @@ void mine(State& s, BlockChain const& _bc) while (!completed) this_thread::sleep_for(chrono::milliseconds(20)); } - +#elif 0 int main() { cnote << "Testing State..."; @@ -224,5 +225,28 @@ int main() return 0; } +#else +int main() +{ + string tempDir = boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()); + + KeyPair myMiner = sha3("Gav's Miner"); + + p2p::Host net("Test"); + cdebug << "Path:" << tempDir; + Client c(&net, tempDir); + + c.setAddress(myMiner.address()); + + this_thread::sleep_for(chrono::milliseconds(1000)); + + c.startMining(); + + this_thread::sleep_for(chrono::milliseconds(6000)); + + c.stopMining(); + + return 0; +} #endif diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index 65e8efcbb..8c1fbb9c7 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -29,7 +29,7 @@ using namespace dev; void Worker::startWorking(IfRunning _ir) { - cnote << "startWorking for thread" << m_name; +// cnote << "startWorking for thread" << m_name; Guard l(x_work); if (m_work && m_work->joinable()) @@ -56,7 +56,7 @@ void Worker::startWorking(IfRunning _ir) void Worker::stopWorking() { - cnote << "stopWorking for thread" << m_name; +// cnote << "stopWorking for thread" << m_name; Guard l(x_work); if (!m_work || !m_work->joinable()) return; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index a048b0238..51a0ff6f6 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -73,11 +73,12 @@ public: * @param _finder The miner that found it. * @return true iff the solution was good (implying that mining should be . */ - virtual bool submitProof(Solution const& _p, WorkPackage& io_wp, Miner* _finder) = 0; + virtual bool submitProof(Solution const& _p, Miner* _finder) = 0; }; /** * @brief A miner - a member and adoptee of the Farm. + * @warning Not threadsafe. It is assumed Farm will synchronise calls to/from this class. */ template class GenericMiner { @@ -96,15 +97,17 @@ public: void setWork(WorkPackage const& _work = WorkPackage()) { - Guard l(x_work); auto old = m_work; - m_work = _work; - if (!!m_work) + { + Guard l(x_work); + m_work = _work; + } + if (!!_work) { pause(); kickOff(); } - else if (!m_work && !!old) + else if (!_work && !!old) pause(); m_hashCount = 0; } @@ -137,16 +140,18 @@ protected: */ bool submitProof(Solution const& _s) { - if (m_farm) + if (!m_farm) + return true; + if (m_farm->submitProof(_s, this)) { - if (!m_farm->submitProof(_s, m_work, this)) - return false; + Guard l(x_work); + m_work.reset(); return true; } - return true; + return false; } - WorkPackage const& work() const { return m_work; } + WorkPackage const& work() const { Guard l(x_work); return m_work; } void accumulateHashes(unsigned _n) { m_hashCount += _n; } @@ -154,10 +159,10 @@ private: FarmFace* m_farm = nullptr; unsigned m_index; - Mutex x_work; - WorkPackage m_work; - uint64_t m_hashCount = 0; + + WorkPackage m_work; + mutable Mutex x_work; }; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 17fb0bf0d..54fed6b01 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -192,6 +192,27 @@ bool Client::isSyncing() const return false; } +void Client::startedWorking() +{ + // Synchronise the state according to the head of the block chain. + // TODO: currently it contains keys for *all* blocks. Make it remove old ones. + cdebug << "startedWorking()"; + WriteGuard l(x_stateDB); + + cdebug << m_bc.number() << m_bc.currentHash(); + + cdebug << "Pre:" << m_preMine.info(); + cdebug << "Post:" << m_postMine.info(); + cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); + + m_preMine.sync(m_bc); + m_postMine = m_preMine; + + cdebug << "Pre:" << m_preMine.info(); + cdebug << "Post:" << m_postMine.info(); + cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); +} + void Client::doneWorking() { // Synchronise the state according to the head of the block chain. @@ -403,10 +424,11 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) return false; newBlock = m_postMine.blockData(); } - + m_bq.import(&newBlock, m_bc); +/* ImportRoute ir = m_bc.attemptImport(newBlock, m_stateDB); if (!ir.first.empty()) - onChainChanged(ir); + onChainChanged(ir);*/ return true; } @@ -500,9 +522,9 @@ void Client::onChainChanged(ImportRoute const& _ir) m_postMine = m_preMine; changeds.insert(PendingChangedFilter); - x_stateDB.unlock(); + x_stateDB.unlock_shared(); onPostStateChanged(); - x_stateDB.lock(); + x_stateDB.lock_shared(); } } @@ -514,8 +536,12 @@ void Client::onPostStateChanged() cnote << "Post state changed: Restarting mining..."; { WriteGuard l(x_stateDB); + cdebug << "Pre:" << m_preMine.info(); m_postMine.commitToMine(m_bc); m_miningInfo = m_postMine.info(); + cdebug << "Pre:" << m_preMine.info(); + cdebug << "Post:" << m_postMine.info(); + cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); } m_farm.setWork(m_miningInfo); diff --git a/libethereum/Client.h b/libethereum/Client.h index 2235c4459..16033a11f 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -169,7 +169,7 @@ public: /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); } + void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); onPostStateChanged(); } /// Stop mining. /// NOT thread-safe void stopMining() override { m_farm.stop(); } @@ -229,11 +229,14 @@ protected: void noteChanged(h256Set const& _filters); private: + /// Called when Worker is starting. + void startedWorking() override; + /// Do some work. Handles blockchain maintenance and mining. - virtual void doWork(); + void doWork() override; /// Called when Worker is exiting. - virtual void doneWorking(); + void doneWorking() override; /// Magically called when the chain has changed. An import route is provided. /// Called by either submitWork() or in our main thread through syncBlockQueue(). diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 56bbcb9df..afca853ed 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -61,8 +61,7 @@ public: */ void setWork(BlockInfo const& _bi) { - WriteGuard l(x_work); - ReadGuard l2(x_miners); + WriteGuard l(x_minerWork); m_header = _bi; auto p = PoW::package(m_header); if (p.headerHash == m_work.headerHash) @@ -90,14 +89,14 @@ public: */ void stop() { - WriteGuard l(x_miners); + WriteGuard l(x_minerWork); m_miners.clear(); + m_work.reset(); } bool isMining() const { - ReadGuard l(x_miners); - return !m_miners.empty(); + return !!m_work; } /** @@ -109,7 +108,7 @@ public: MiningProgress p; p.ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_lastStart).count(); { - ReadGuard l2(x_miners); + ReadGuard l2(x_minerWork); for (auto const& i: m_miners) p.hashes += i->hashCount(); } @@ -127,7 +126,7 @@ public: */ void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } - WorkPackage work() const { ReadGuard l(x_work); return m_work; } + WorkPackage work() const { ReadGuard l(x_minerWork); return m_work; } private: /** @@ -136,19 +135,14 @@ private: * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ - bool submitProof(Solution const& _s, WorkPackage& _wp, Miner* _m) override + bool submitProof(Solution const& _s, Miner* _m) override { - ReadGuard l(x_work); - if (_wp.headerHash != m_work.headerHash) - return false; - if (m_onSolutionFound && m_onSolutionFound(_s)) { - ReadGuard l(x_miners); + WriteGuard ul(x_minerWork); for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); - _wp.reset(); m_work.reset(); return true; } @@ -161,8 +155,7 @@ private: template bool start() { - ReadGuard l(x_work); - WriteGuard l2(x_miners); + WriteGuard l(x_minerWork); if (!m_miners.empty() && !!std::dynamic_pointer_cast(m_miners[0])) return true; m_miners.clear(); @@ -181,17 +174,15 @@ private: m_lastStart = std::chrono::steady_clock::now(); } - mutable SharedMutex x_miners; + mutable SharedMutex x_minerWork; std::vector> m_miners; + WorkPackage m_work; + BlockInfo m_header; mutable SharedMutex x_progress; mutable MiningProgress m_progress; std::chrono::steady_clock::time_point m_lastStart; - mutable SharedMutex x_work; - WorkPackage m_work; - BlockInfo m_header; - SolutionFound m_onSolutionFound; }; From cfd8274097952bd4490f2fdeef0d8d97f2fc5820 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 10:18:34 +0200 Subject: [PATCH 14/34] AlethZero fixes. --- eth/main.cpp | 45 ++++++++++++++++++++-------------------- libethcore/BlockInfo.cpp | 9 ++++++-- libethcore/BlockInfo.h | 1 + libethcore/EthashAux.cpp | 29 +++++++++++++++++++++++--- libethcore/EthashAux.h | 4 +++- 5 files changed, 60 insertions(+), 28 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 0ac53fab5..24bf839aa 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -142,12 +142,10 @@ void help() << " -R,--rebuild First rebuild the blockchain from the existing database." << endl << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl + << " -S,--temporary-secret Set the secret key for use with send command, for this session only." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl << " -V,--version Show the version and exit." << endl -#if ETH_EVMJIT - << " --jit Use EVM JIT (default: off)." << endl -#endif ; exit(0); } @@ -274,8 +272,9 @@ int main(int argc, char** argv) /// Mining params unsigned mining = ~(unsigned)0; bool forceMining = false; - KeyPair us = KeyPair::create(); - Address coinbase = us.address(); + KeyPair sigKey = KeyPair::create(); + Secret sessionSecret; + Address coinbase = sigKey.address(); /// Structured logging params bool structuredLogging = false; @@ -291,7 +290,7 @@ int main(int argc, char** argv) if (b.size()) { RLP config(b); - us = KeyPair(config[0].toHash()); + sigKey = KeyPair(config[0].toHash()); coinbase = config[1].toHash
(); } @@ -374,7 +373,9 @@ int main(int argc, char** argv) return -1; } else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) - us = KeyPair(h256(fromHex(argv[++i]))); + sigKey = KeyPair(h256(fromHex(argv[++i]))); + else if ((arg == "-S" || arg == "--session-secret") && i + 1 < argc) + sessionSecret = h256(fromHex(argv[++i])); else if (arg == "--structured-logging-format" && i + 1 < argc) structuredLoggingFormat = string(argv[++i]); else if (arg == "--structured-logging") @@ -510,10 +511,13 @@ int main(int argc, char** argv) { RLPStream config(2); - config << us.secret() << coinbase; + config << sigKey.secret() << coinbase; writeFile(configFile, config.out()); } + if (sessionSecret) + sigKey = KeyPair(sessionSecret); + // Two codepaths is necessary since named block require database, but numbered // blocks are superuseful to have when database is already open in another process. if (mode == OperationMode::DAGInit && !(initDAG == LatestBlock || initDAG == PendingBlock)) @@ -626,7 +630,7 @@ int main(int argc, char** argv) c->setAddress(coinbase); } - cout << "Transaction Signer: " << us.address() << endl; + cout << "Transaction Signer: " << sigKey.address() << endl; cout << "Mining Benefactor: " << coinbase << endl; web3.startNetwork(); @@ -641,8 +645,7 @@ int main(int argc, char** argv) if (jsonrpc > -1) { jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); - jsonrpcServer->setIdentities({us}); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({sigKey}))); jsonrpcServer->StartListening(); } #endif @@ -766,8 +769,7 @@ int main(int argc, char** argv) if (jsonrpc < 0) jsonrpc = SensibleHttpPort; jsonrpcConnector = unique_ptr(new jsonrpc::HttpServer(jsonrpc, "", "", SensibleHttpThreads)); - jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({us}))); - jsonrpcServer->setIdentities({us}); + jsonrpcServer = shared_ptr(new WebThreeStubServer(*jsonrpcConnector.get(), web3, vector({sigKey}))); jsonrpcServer->StartListening(); } else if (cmd == "jsonstop") @@ -779,12 +781,11 @@ int main(int argc, char** argv) #endif else if (cmd == "address") { - cout << "Current address:" << endl - << toHex(us.address().asArray()) << endl; + cout << "Current address:" << endl << sigKey.address() << endl; } else if (cmd == "secret") { - cout << "Secret Key: " << toHex(us.secret().asArray()) << endl; + cout << "Secret Key: " << sigKey.secret() << endl; } else if (c && cmd == "block") { @@ -799,7 +800,7 @@ int main(int argc, char** argv) } else if (c && cmd == "balance") { - cout << "Current balance: " << formatBalance( c->balanceAt(us.address())) << " = " <balanceAt(us.address()) << " wei" << endl; + cout << "Current balance: " << formatBalance( c->balanceAt(sigKey.address())) << " = " <balanceAt(sigKey.address()) << " wei" << endl; } else if (c && cmd == "transact") { @@ -915,7 +916,7 @@ int main(int argc, char** argv) try { Address dest = h160(fromHex(hexAddr, WhenError::Throw)); - c->submitTransaction(us.secret(), amount, dest, bytes(), minGas); + c->submitTransaction(sigKey.secret(), amount, dest, bytes(), minGas); } catch (BadHexCharacter& _e) { @@ -984,7 +985,7 @@ int main(int argc, char** argv) else if (gas < minGas) cwarn << "Minimum gas amount is" << minGas; else - c->submitTransaction(us.secret(), endowment, init, gas, gasPrice); + c->submitTransaction(sigKey.secret(), endowment, init, gas, gasPrice); } else cwarn << "Require parameters: contract ENDOWMENT GASPRICE GAS CODEHEX"; @@ -1101,7 +1102,7 @@ int main(int argc, char** argv) { string hexSec; iss >> hexSec; - us = KeyPair(h256(fromHex(hexSec))); + sigKey = KeyPair(h256(fromHex(hexSec))); } else cwarn << "Require parameter: setSecret HEXSECRETKEY"; @@ -1141,7 +1142,7 @@ int main(int argc, char** argv) string path; iss >> path; RLPStream config(2); - config << us.secret() << coinbase; + config << sigKey.secret() << coinbase; writeFile(path, config.out()); } else @@ -1157,7 +1158,7 @@ int main(int argc, char** argv) if (b.size()) { RLP config(b); - us = KeyPair(config[0].toHash()); + sigKey = KeyPair(config[0].toHash()); coinbase = config[1].toHash
(); } else diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 6cd431931..b45bdc57e 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -23,6 +23,7 @@ #include #include #include +#include "EthashAux.h" #include "ProofOfWork.h" #include "Exceptions.h" #include "Params.h" @@ -63,8 +64,7 @@ void BlockInfo::clear() h256 const& BlockInfo::seedHash() const { if (!m_seedHash) - for (u256 n = number; n >= c_epochDuration; n -= c_epochDuration) - m_seedHash = sha3(m_seedHash); + m_seedHash = EthashAux::seedHash((unsigned)number); return m_seedHash; } @@ -145,9 +145,14 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const throw; } + if (number > ~(unsigned)0) + throw InvalidNumber(); + // check it hashes according to proof of work or that it's the genesis block. if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this)) BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty)); + else if (_s == QuickNonce && parentHash && !ProofOfWork::preVerify(*this)) + BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty)); if (_s != CheckNothing) { diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index dffff73f4..79c12ebb4 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -39,6 +39,7 @@ enum IncludeNonce enum Strictness { CheckEverything, + QuickNonce, IgnoreNonce, CheckNothing }; diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 969310dac..fb4c2820d 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -63,24 +63,47 @@ ethash_params EthashAux::params(unsigned _n) return p; } +h256 EthashAux::seedHash(unsigned _number) +{ + unsigned epoch = _number / ETHASH_EPOCH_LENGTH; + RecursiveGuard l(get()->x_this); + if (_number < get()->m_seedHashes.size()) + return get()->m_seedHashes[_number]; + h256 ret; + unsigned n = 0; + if (!get()->m_seedHashes.empty()) + { + ret = get()->m_seedHashes.back(); + n = get()->m_seedHashes.size() - 1; + } + cdebug << "Searching for seedHash of epoch " << epoch; + for (; n < epoch; ++n, ret = sha3(ret)) + cdebug << "Epoch" << n << "is" << ret.abridged(); + return ret; +} + ethash_params EthashAux::params(h256 const& _seedHash) { RecursiveGuard l(get()->x_this); unsigned epoch = 0; try { - epoch = get()->m_seedHashes.at(_seedHash); + epoch = get()->m_epochs.at(_seedHash); } catch (...) { - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} + cdebug << "Searching for seedHash " << _seedHash.abridged(); + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h)) + { + cdebug << "Epoch" << epoch << "is" << h.abridged(); + } if (epoch == 2048) { std::ostringstream error; error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } - get()->m_seedHashes[_seedHash] = epoch; + get()->m_epochs[_seedHash] = epoch; } return params(epoch * ETHASH_EPOCH_LENGTH); } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index aec1089a2..94c2243e0 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -36,6 +36,7 @@ public: using LightType = void const*; using FullType = void const*; + static h256 seedHash(unsigned _number); static ethash_params params(BlockInfo const& _header); static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); @@ -58,7 +59,8 @@ private: std::map m_lights; std::map m_fulls; - std::map m_seedHashes; + std::map m_epochs; + h256s m_seedHashes; }; } From 1b05e078e48bf9b6eb64218d956f9b07c0b55bd3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 10:37:11 +0200 Subject: [PATCH 15/34] Marginally better Client async code. --- libethereum/Client.cpp | 14 ++++---------- libethereum/Client.h | 9 ++++----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 54fed6b01..c99b13425 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -571,19 +571,13 @@ void Client::doWork() { // TODO: Use condition variable rather than this rubbish. - Guard l(x_fakeSignalSystemState); - - if (m_syncTransactionQueue) - { - m_syncTransactionQueue = false; + bool t = true; + if (m_syncTransactionQueue.compare_exchange_strong(t, false)) syncTransactionQueue(); - } - if (m_syncBlockQueue) - { - m_syncBlockQueue = false; + t = true; + if (m_syncBlockQueue.compare_exchange_strong(t, false)) syncBlockQueue(); - } checkWatchGarbage(); diff --git a/libethereum/Client.h b/libethereum/Client.h index 16033a11f..289df3fe0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -249,10 +249,10 @@ private: void syncTransactionQueue(); /// Magically called when m_tq needs syncing. Be nice and don't block. - void onTransactionQueueReady() { Guard l(x_fakeSignalSystemState); m_syncTransactionQueue = true; } + void onTransactionQueueReady() { m_syncTransactionQueue = true; } /// Magically called when m_tq needs syncing. Be nice and don't block. - void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; } + void onBlockQueueReady() { m_syncBlockQueue = true; } /// Called when the post state has changed (i.e. when more transactions are in it or we're mining on a new block). /// This updates m_miningInfo. @@ -286,9 +286,8 @@ private: ///< When did we last both doing GC on the watches? // TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables) - mutable Mutex x_fakeSignalSystemState; - bool m_syncTransactionQueue = false; - bool m_syncBlockQueue = false; + std::atomic m_syncTransactionQueue = {false}; + std::atomic m_syncBlockQueue = {false}; }; } From 8e3ef1ee5c1e3ea522a89e64136a9d61d84b7109 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 12:43:00 +0200 Subject: [PATCH 16/34] Fix to ethash seedHash caching. --- alethzero/Main.ui | 6 ++++ alethzero/MainWin.cpp | 5 +++ alethzero/MainWin.h | 1 + eth/main.cpp | 41 +++++++++++++++++++++- libdevcore/Guards.h | 71 ++++++++++++++++++++++++++++++++++++++ libethcore/EthashAux.cpp | 33 +++++++++--------- libethcore/EthashAux.h | 2 +- libethcore/Params.cpp | 1 - libethcore/Params.h | 1 - libethereum/BlockQueue.cpp | 12 +++++++ libethereum/BlockQueue.h | 3 ++ libethereum/Client.cpp | 47 ++++++++++++++----------- libethereum/Client.h | 4 ++- libethereum/Farm.h | 6 +++- 14 files changed, 190 insertions(+), 43 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 3f3d1e237..8e48793c9 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -180,6 +180,7 @@ + @@ -1679,6 +1680,11 @@ font-size: 14pt &GPU Mining + + + Retry Unknown Parent Blocks + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2e4478594..a60875ba6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1752,6 +1752,11 @@ void Main::on_clearPending_triggered() refreshAll(); } +void Main::on_retryUnknown_triggered() +{ + ethereum()->retryUnkonwn(); +} + void Main::on_killBlockchain_triggered() { writeSettings(); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index a5c74eeaa..a8579ed01 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -163,6 +163,7 @@ private slots: void on_usePrivate_triggered(); void on_turboMining_triggered(); void on_jitvm_triggered(); + void on_retryUnknown_triggered(); // Debugger void on_debugCurrent_triggered(); diff --git a/eth/main.cpp b/eth/main.cpp index 24bf839aa..461494cef 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -32,11 +32,12 @@ #include #include #include +#include +#include #include #include #include #include -#include #if ETH_READLINE #include #include @@ -111,6 +112,7 @@ void help() << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -B,--block-fees Set the block fee profit in the reference unit e.g. ยข (Default: 15)." << endl << " -c,--client-name Add a name to your client's version string (default: blank)." << endl + << " -C,--check-pow Check PoW credentials for validity." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl << " -D,--create-dag Create the DAG in preparation for mining on given block and exit." << endl @@ -401,6 +403,43 @@ int main(int argc, char** argv) return -1; } } + else if ((arg == "-C" || arg == "--check-pow") && i + 4 < argc) + { + string m; + try + { + BlockInfo bi; + m = boost::to_lower_copy(string(argv[++i])); + h256 powHash(m); + m = boost::to_lower_copy(string(argv[++i])); + h256 seedHash; + if (m.size() == 64 || m.size() == 66) + seedHash = h256(m); + else + seedHash = EthashAux::seedHash(stol(m)); + m = boost::to_lower_copy(string(argv[++i])); + bi.difficulty = u256(m); + auto boundary = bi.boundary(); + m = boost::to_lower_copy(string(argv[++i])); + bi.nonce = h64(m); + auto r = EthashAux::eval(seedHash, powHash, bi.nonce); + bool valid = r.value < boundary; + cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; + cout << r.value << (valid ? " < " : " >= ") << boundary << endl; + cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl; + cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl; + cout << " with seed as " << seedHash << endl; + if (valid) + cout << "(mixHash = " << r.mixHash << ")" << endl; + cout << "SHA3( light(seed) ) = " << sha3(bytesConstRef((byte const*)EthashAux::light(seedHash), EthashAux::params(seedHash).cache_size)) << endl; + exit(0); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << m << endl; + return -1; + } + } else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) { try diff --git a/libdevcore/Guards.h b/libdevcore/Guards.h index f5c64b041..4229428ce 100644 --- a/libdevcore/Guards.h +++ b/libdevcore/Guards.h @@ -38,4 +38,75 @@ using UpgradableGuard = boost::upgrade_lock; using UpgradeGuard = boost::upgrade_to_unique_lock; using WriteGuard = boost::unique_lock; +template +struct GenericGuardBool: GuardType +{ + GenericGuardBool(MutexType& _m): GuardType(_m) {} + bool b = true; +}; +template +struct GenericUnguardBool +{ + GenericUnguardBool(MutexType& _m): m(_m) { m.unlock(); } + ~GenericUnguardBool() { m.lock(); } + bool b = true; + MutexType& m; +}; +template +struct GenericUnguardSharedBool +{ + GenericUnguardSharedBool(MutexType& _m): m(_m) { m.unlock_shared(); } + ~GenericUnguardSharedBool() { m.lock_shared(); } + bool b = true; + MutexType& m; +}; + +/** @brief Simple block guard. + * The expression/block following is guarded though the given mutex. + * Usage: + * @code + * Mutex m; + * unsigned d; + * ... + * ETH_GUARDED(m) d = 1; + * ... + * ETH_GUARDED(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; } + * @endcode + * + * There are several variants of this basic mechanism for different Mutex types and Guards. + * + * There is also the UNGUARD variant which allows an unguarded expression/block to exist within a + * guarded expression. eg: + * + * @code + * Mutex m; + * int d; + * ... + * ETH_GUARDED(m) + * { + * for (auto d = 50; d > 25; --d) + * foo(d); + * ETH_UNGUARDED(m) + * bar(); + * for (; d > 0; --d) + * foo(d); + * } + * @endcode + */ + +#define ETH_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_READ_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_WRITE_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_RECURSIVE_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_UNGUARDED(MUTEX) \ + for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_READ_UNGUARDED(MUTEX) \ + for (GenericUnguardSharedBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_WRITE_UNGUARDED(MUTEX) \ + for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) + } diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index fb4c2820d..061af566e 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -67,19 +67,24 @@ h256 EthashAux::seedHash(unsigned _number) { unsigned epoch = _number / ETHASH_EPOCH_LENGTH; RecursiveGuard l(get()->x_this); - if (_number < get()->m_seedHashes.size()) - return get()->m_seedHashes[_number]; - h256 ret; - unsigned n = 0; - if (!get()->m_seedHashes.empty()) + if (epoch >= get()->m_seedHashes.size()) { - ret = get()->m_seedHashes.back(); - n = get()->m_seedHashes.size() - 1; + h256 ret; + unsigned n = 0; + if (!get()->m_seedHashes.empty()) + { + ret = get()->m_seedHashes.back(); + n = get()->m_seedHashes.size() - 1; + } + get()->m_seedHashes.resize(epoch + 1); + cdebug << "Searching for seedHash of epoch " << epoch; + for (; n <= epoch; ++n, ret = sha3(ret)) + { + get()->m_seedHashes[n] = ret; + cdebug << "Epoch" << n << "is" << ret.abridged(); + } } - cdebug << "Searching for seedHash of epoch " << epoch; - for (; n < epoch; ++n, ret = sha3(ret)) - cdebug << "Epoch" << n << "is" << ret.abridged(); - return ret; + return get()->m_seedHashes[epoch]; } ethash_params EthashAux::params(h256 const& _seedHash) @@ -93,17 +98,13 @@ ethash_params EthashAux::params(h256 const& _seedHash) catch (...) { cdebug << "Searching for seedHash " << _seedHash.abridged(); - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h)) - { - cdebug << "Epoch" << epoch << "is" << h.abridged(); - } + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} if (epoch == 2048) { std::ostringstream error; error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } - get()->m_epochs[_seedHash] = epoch; } return params(epoch * ETHASH_EPOCH_LENGTH); } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index 94c2243e0..c927a012b 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -41,7 +41,7 @@ public: static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); static LightType light(BlockInfo const& _header); - static LightType light(h256 const& _header); + static LightType light(h256 const& _seedHash); static bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); static bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); diff --git a/libethcore/Params.cpp b/libethcore/Params.cpp index 029f8b47a..655c8a78b 100644 --- a/libethcore/Params.cpp +++ b/libethcore/Params.cpp @@ -30,7 +30,6 @@ namespace eth //--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json u256 const c_genesisDifficulty = 131072; u256 const c_maximumExtraDataSize = 1024; -u256 const c_epochDuration = 30000; u256 const c_genesisGasLimit = 3141592; u256 const c_minGasLimit = 125000; u256 const c_gasLimitBoundDivisor = 1024; diff --git a/libethcore/Params.h b/libethcore/Params.h index 46b30e2c3..b957f9737 100644 --- a/libethcore/Params.h +++ b/libethcore/Params.h @@ -37,7 +37,6 @@ extern u256 const c_minimumDifficulty; extern u256 const c_difficultyBoundDivisor; extern u256 const c_durationLimit; extern u256 const c_maximumExtraDataSize; -extern u256 const c_epochDuration; extern u256 const c_stackLimit; extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them. diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index e9dd99cd1..b76e4bed6 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -183,3 +183,15 @@ void BlockQueue::noteReadyWithoutWriteGuard(h256 _good) m_unknown.erase(r.first, r.second); } } + +void BlockQueue::retryAllUnknown() +{ + for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) + { + m_ready.push_back(it->second.second); + auto newReady = it->second.first; + m_unknownSet.erase(newReady); + m_readySet.insert(newReady); + } + m_unknown.clear(); +} diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index f5cdf7ab5..4a503d114 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -71,6 +71,9 @@ public: /// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain). void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); } + /// Force a retry of all the blocks with unknown parents. + void retryAllUnknown(); + /// Get information on the items queued. std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index c99b13425..0660b6cee 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -436,15 +436,12 @@ void Client::syncBlockQueue() { ImportRoute ir; + cwork << "BQ ==> CHAIN ==> STATE"; { WriteGuard l(x_stateDB); - cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; - x_stateDB.unlock(); - - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); - - x_stateDB.lock(); + ETH_WRITE_UNGUARDED(x_stateDB) + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); if (ir.first.empty()) return; m_stateDB = db; @@ -458,7 +455,11 @@ void Client::syncTransactionQueue() cwork << "postSTATE <== TQ"; h256Set changeds; - TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + TransactionReceipts newPendingReceipts; + + ETH_WRITE_GUARDED(x_stateDB) + newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + if (newPendingReceipts.size()) { for (size_t i = 0; i < newPendingReceipts.size(); i++) @@ -512,8 +513,7 @@ void Client::onChainChanged(ImportRoute const& _ir) // RESTART MINING // LOCKS REALLY NEEDED? - { - ReadGuard l(x_stateDB); + ETH_WRITE_GUARDED(x_stateDB) if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { if (isMining()) @@ -522,11 +522,9 @@ void Client::onChainChanged(ImportRoute const& _ir) m_postMine = m_preMine; changeds.insert(PendingChangedFilter); - x_stateDB.unlock_shared(); - onPostStateChanged(); - x_stateDB.lock_shared(); + ETH_WRITE_UNGUARDED(x_stateDB) + onPostStateChanged(); } - } noteChanged(changeds); } @@ -534,17 +532,24 @@ void Client::onChainChanged(ImportRoute const& _ir) void Client::onPostStateChanged() { cnote << "Post state changed: Restarting mining..."; + if (isMining()) { - WriteGuard l(x_stateDB); - cdebug << "Pre:" << m_preMine.info(); - m_postMine.commitToMine(m_bc); - m_miningInfo = m_postMine.info(); - cdebug << "Pre:" << m_preMine.info(); - cdebug << "Post:" << m_postMine.info(); - cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); + { + WriteGuard l(x_stateDB); + m_postMine.commitToMine(m_bc); + m_miningInfo = m_postMine.info(); + } + m_farm.setWork(m_miningInfo); } +} - m_farm.setWork(m_miningInfo); +void Client::startMining() +{ + if (m_turboMining) + m_farm.startGPU(); + else + m_farm.startCPU(); + onPostStateChanged(); } void Client::noteChanged(h256Set const& _filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index 289df3fe0..dedb3bcf1 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -169,7 +169,7 @@ public: /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); onPostStateChanged(); } + void startMining() override; /// Stop mining. /// NOT thread-safe void stopMining() override { m_farm.stop(); } @@ -202,6 +202,8 @@ public: void clearPending(); /// Kills the blockchain. Just for debug use. void killChain(); + /// Retries all blocks with unknown parents. + void retryUnkonwn() { m_bq.retryAllUnknown(); } protected: /// InterfaceStub methods diff --git a/libethereum/Farm.h b/libethereum/Farm.h index afca853ed..26d4b139e 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -92,11 +92,12 @@ public: WriteGuard l(x_minerWork); m_miners.clear(); m_work.reset(); + m_isMining = false; } bool isMining() const { - return !!m_work; + return m_isMining; } /** @@ -165,6 +166,7 @@ private: m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); m_miners.back()->setWork(m_work); } + m_isMining = true; resetTimer(); return true; } @@ -179,6 +181,8 @@ private: WorkPackage m_work; BlockInfo m_header; + std::atomic m_isMining = {false}; + mutable SharedMutex x_progress; mutable MiningProgress m_progress; std::chrono::steady_clock::time_point m_lastStart; From ecfe6acc95f75a1039a4a8853b767b3b68722e29 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 12:54:03 +0200 Subject: [PATCH 17/34] Version bump. Minor fix. --- libdevcore/Common.cpp | 2 +- libethereum/Client.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index b6e8e7f93..78b3d9c30 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.7"; +char const* Version = "0.9.8"; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 0660b6cee..df6815d9e 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -332,7 +332,8 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) void Client::setForceMining(bool _enable) { m_forceMining = _enable; - startMining(); + if (isMining()) + startMining(); } MiningProgress Client::miningProgress() const From 6cf2a93e20f4d1e459364543385449c36f17250f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 11 Apr 2015 15:52:36 +0200 Subject: [PATCH 18/34] Some early refactoring to support async miners better. --- exp/main.cpp | 2 +- libethcore/ProofOfWork.cpp | 30 ++++++--- libethcore/ProofOfWork.h | 22 +++---- libethereum/Client.cpp | 2 +- libethereum/Client.h | 4 +- libethereum/ClientBase.h | 2 +- libethereum/Interface.h | 2 +- libethereum/Miner.cpp | 6 +- libethereum/Miner.h | 77 +++++++++++++++++++++++ libethereum/State.cpp | 2 +- libethereum/State.h | 4 +- libweb3jsonrpc/WebThreeStubServerBase.cpp | 2 +- mix/MixClient.h | 2 +- test/blockchain.cpp | 4 +- 14 files changed, 123 insertions(+), 38 deletions(-) diff --git a/exp/main.cpp b/exp/main.cpp index 48562f80e..88f1075a9 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -114,7 +114,7 @@ int main() BlockInfo genesis = CanonBlockChain::genesis(); genesis.difficulty = 1 << 18; cdebug << (h256)u256((bigint(1) << 256) / genesis.difficulty); - std::pair r; + std::pair r; while (!r.first.completed) r = ecl.mine(genesis, 1000); cdebug << r.second.mixHash << r.second.nonce; diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index f7cf4944b..ffa787e3e 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -50,16 +50,16 @@ bool EthashPoW::verify(BlockInfo const& _header) return Ethasher::verify(_header); } -std::pair EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) +std::pair EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) { Ethasher::Miner m(_header); - std::pair ret; + std::pair ret; auto tid = std::this_thread::get_id(); static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng)); - h256 boundary = u256((bigint(1) << 256) / _header.difficulty); + h256 boundary = _header.boundary(); ret.first.requirement = log2((double)(u256)boundary); // 2^ 0 32 64 128 256 @@ -68,7 +68,7 @@ std::pair EthashCPU::mine(BlockInfo const& _header, // evaluate until we run out of time auto startTime = std::chrono::steady_clock::now(); double best = 1e99; // high enough to be effectively infinity :) - Proof result; + Solution result; unsigned hashCount = 0; for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++) { @@ -128,7 +128,7 @@ struct EthashCLHook: public ethash_cl_miner::search_hook { if (m_aborted) return; - cdebug << "Attempting to abort"; +// cdebug << "Attempting to abort"; m_abort = true; for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) std::this_thread::sleep_for(chrono::milliseconds(30)); @@ -148,14 +148,14 @@ protected: for (unsigned i = 0; i < _count; ++i) m_found.push_back((Nonce)(u64)_nonces[i]); m_aborted = true; - cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); +// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); return true; } virtual bool searched(uint64_t _startNonce, uint32_t _count) override { Guard l(x_all); - cdebug << "Searched" << _count << "from" << _startNonce; +// cdebug << "Searched" << _count << "from" << _startNonce; m_total += _count; m_last = _startNonce + _count; if (m_abort) @@ -184,7 +184,7 @@ EthashCL::~EthashCL() { } -std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) +std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) { if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash()) { @@ -206,7 +206,14 @@ std::pair EthashCL::mine(BlockInfo const& _header, unsi } m_lastHeader = _header; + MineInfo mi; + Solution proof; + mi.requirement = log2((double)(u256)_header.boundary()); + mi.best = 0; + std::this_thread::sleep_for(chrono::milliseconds(_msTimeout)); + + mi.hashes += m_hook->fetchTotal(); auto found = m_hook->fetchFound(); if (!found.empty()) { @@ -214,10 +221,13 @@ std::pair EthashCL::mine(BlockInfo const& _header, unsi { auto result = Ethasher::eval(_header, n); if (result.value < _header.boundary()) - return std::make_pair(MineInfo(true), EthashCL::Proof{n, result.mixHash}); + { + mi.completed = true; + proof = Solution{n, result.mixHash}; + } } } - return std::make_pair(MineInfo(false), EthashCL::Proof()); + return std::make_pair(mi, proof); } #endif diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 2e04e842c..bd8ab58db 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -54,23 +54,23 @@ struct MineInfo class EthashPoW { public: - struct Proof + struct Solution { Nonce nonce; h256 mixHash; }; static bool verify(BlockInfo const& _header); - static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } virtual unsigned defaultTimeout() const { return 100; } - virtual std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0; + virtual std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0; }; class EthashCPU: public EthashPoW { public: - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; + std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; protected: Nonce m_last; @@ -85,7 +85,7 @@ public: EthashCL(); ~EthashCL(); - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; + std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; unsigned defaultTimeout() const override { return 500; } protected: @@ -105,11 +105,11 @@ template class ProofOfWorkEngine: public Evaluator { public: - using Proof = Nonce; + using Solution = Nonce; static bool verify(BlockInfo const& _header) { return (bigint)(u256)Evaluator::eval(_header.headerHash(WithoutNonce), _header.nonce) <= (bigint(1) << 256) / _header.difficulty; } - inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true); - static void assignResult(Proof const& _r, BlockInfo& _header) { _header.nonce = _r; } + inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true); + static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r; } unsigned defaultTimeout() const { return 100; } protected: @@ -127,7 +127,7 @@ using SHA3ProofOfWork = ProofOfWorkEngine; using ProofOfWork = Ethash; template -std::pair::Proof> ProofOfWorkEngine::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) +std::pair::Solution> ProofOfWorkEngine::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) { auto headerHashWithoutNonce = _header.headerHash(WithoutNonce); auto difficulty = _header.difficulty; @@ -145,11 +145,11 @@ std::pair::Proof> ProofOfWorkEng // evaluate until we run out of time auto startTime = std::chrono::steady_clock::now(); double best = 1e99; // high enough to be effectively infinity :) - ProofOfWorkEngine::Proof solution; + ProofOfWorkEngine::Solution solution; unsigned h = 0; for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++) { - solution = (ProofOfWorkEngine::Proof)s; + solution = (ProofOfWorkEngine::Solution)s; auto e = (bigint)(u256)Evaluator::eval(headerHashWithoutNonce, solution); best = std::min(best, log2((double)e)); if (e <= d) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index d8723430d..b2f5ff63c 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -452,7 +452,7 @@ pair Client::getWork() return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); } -bool Client::submitWork(ProofOfWork::Proof const& _proof) +bool Client::submitWork(ProofOfWork::Solution const& _proof) { Guard l(x_remoteMiner); return m_remoteMiner.submitWork(_proof); diff --git a/libethereum/Client.h b/libethereum/Client.h index cc51f9747..ec852afd2 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -82,7 +82,7 @@ public: h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); } u256 const& difficulty() const { return m_state.info().difficulty; } - bool submitWork(ProofOfWork::Proof const& _result) { return (m_isComplete = m_state.completeMine(_result)); } + bool submitWork(ProofOfWork::Solution const& _result) { return (m_isComplete = m_state.completeMine(_result)); } virtual bool isComplete() const override { return m_isComplete; } virtual bytes const& blockData() const { return m_state.blockData(); } @@ -216,7 +216,7 @@ public: /// nonce (the 'work hash') and the difficulty to be met. virtual std::pair getWork() override; /// Submit the proof for the proof-of-work. - virtual bool submitWork(ProofOfWork::Proof const& _proof) override; + virtual bool submitWork(ProofOfWork::Solution const& _proof) override; // Debug stuff: diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ddfbf1176..ae6d27578 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -150,7 +150,7 @@ public: virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::hashrate")); } virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); } virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } - virtual bool submitWork(eth::ProofOfWork::Proof const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } + virtual bool submitWork(eth::ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } State asOf(BlockNumber _h) const; diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 02833743e..cf2e7f5ea 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -190,7 +190,7 @@ public: /// Get hash of the current block to be mined minus the nonce (the 'work hash'). virtual std::pair getWork() = 0; /// Submit the nonce for the proof-of-work. - virtual bool submitWork(ProofOfWork::Proof const& _proof) = 0; + virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0; /// Check the progress of the mining. virtual MineProgress miningProgress() const = 0; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index b3a65f081..dc3d9bd9e 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -30,11 +30,9 @@ using namespace dev::eth; Miner::~Miner() {} -LocalMiner::LocalMiner(MinerHost* _host, unsigned _id): - AsyncMiner(_host, _id), - Worker("miner-" + toString(_id)) +LocalMiner::LocalMiner(MinerHost* _host, unsigned _id) { - m_pow.reset(_host->turbo() ? new Ethash : (Ethash*)new EthashCPU); + setup(_host, _id); } void LocalMiner::setup(MinerHost* _host, unsigned _id) diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 8c2fc4bb4..86d103db5 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -36,6 +36,15 @@ namespace dev namespace eth { +struct WorkPackage +{ + h256 boundary; + h256 headerHash; ///< When h256() means "pause until notified a new work package is available". + h256 seedHash; +}; + +static const WorkPackage NullWorkPackage; + /** * @brief Describes the progress of a mining operation. */ @@ -57,6 +66,10 @@ struct MineProgress class MinerHost { public: + // ============================= NEW API ============================= + virtual WorkPackage const& getWork() const { return NullWorkPackage; } + + // ============================= OLD API ============================= virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. virtual void onProgressed() {} ///< Called once some progress has been made. virtual void onComplete() {} ///< Called once a block is found. @@ -174,5 +187,69 @@ private: std::list m_mineHistory; ///< What the history of our mining? }; +/** + * @brief A collective of Miners. + * Miners ask for work, then submit proofs + * @threadsafe + */ +class Farm: public MinerHost +{ +public: + /** + * @brief Sets the current mining mission. + * @param _bi The block (header) we wish to be mining. + */ + void setWork(BlockInfo const& _bi); + + /** + * @brief (Re)start miners for CPU only. + * @returns true if started properly. + */ + bool startCPU(); + + /** + * @brief (Re)start miners for GPU only. + * @returns true if started properly. + */ + bool startGPU(); + + /** + * @brief Stop all mining activities. + */ + void stop(); + + /** + * @brief Get information on the progress of mining this work package. + * @return The progress with mining so far. + */ + MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } + +protected: + /** + * @brief Called by a Miner to retrieve a work package. Reimplemented from MinerHost. + * @return The work package to solve. + */ + virtual WorkPackage const& getWork() const override { ReadGuard l(x_work); return m_work; } + + /** + * @brief Called from a Miner to note a WorkPackage has a solution. + * @param _p The solution. + * @param _wp The WorkPackage that the Solution is for. + * @return true iff the solution was good (implying that mining should be . + */ + virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; + +private: + mutable SharedMutex x_miners; + std::vector> m_miners; + + mutable SharedMutex x_progress; + MineProgress m_progress; + + mutable SharedMutex x_work; + WorkPackage m_work; +}; + + } } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 6d3301cd7..e77565837 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -856,7 +856,7 @@ void State::commitToMine(BlockChain const& _bc) m_committedToMine = true; } -bool State::completeMine(ProofOfWork::Proof const& _nonce) +bool State::completeMine(ProofOfWork::Solution const& _nonce) { ProofOfWork::assignResult(_nonce, m_currentBlock); diff --git a/libethereum/State.h b/libethereum/State.h index 1b71038b4..b327378a1 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -162,7 +162,7 @@ public: /// Pass in a solution to the proof-of-work. /// @returns true iff the given nonce is a proof-of-work for this State's block. - bool completeMine(ProofOfWork::Proof const& _result); + bool completeMine(ProofOfWork::Solution const& _result); /// Attempt to find valid nonce for block that this state represents. /// This function is thread-safe. You can safely have other interactions with this object while it is happening. @@ -174,7 +174,7 @@ public: m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); MineInfo ret; - typename ProofOfWork::Proof r; + typename ProofOfWork::Solution r; std::tie(ret, r) = _pow->mine(m_currentBlock, _pow->defaultTimeout(), true); if (!ret.completed) diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 5b6c833bc..7e7b83e3c 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -767,7 +767,7 @@ bool WebThreeStubServerBase::eth_submitWork(string const& _nonce, string const& { try { - return client()->submitWork(ProofOfWork::Proof{jsToFixed(_nonce), jsToFixed<32>(_mixHash)}); + return client()->submitWork(ProofOfWork::Solution{jsToFixed(_nonce), jsToFixed<32>(_mixHash)}); } catch (...) { diff --git a/mix/MixClient.h b/mix/MixClient.h index 93a674d96..357e22930 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -71,7 +71,7 @@ public: uint64_t hashrate() const override; eth::MineProgress miningProgress() const override; std::pair getWork() override { return std::pair(); } - bool submitWork(eth::ProofOfWork::Proof const&) override { return false; } + bool submitWork(eth::ProofOfWork::Solution const&) override { return false; } virtual void flushTransactions() override {} /// @returns the last mined block information diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 97171e3f2..8f5605898 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -587,7 +587,7 @@ void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj) _currentBlockHeader = tmp; ProofOfWork pow; - std::pair ret; + std::pair ret; while (!ProofOfWork::verify(_currentBlockHeader)) { ret = pow.mine(_currentBlockHeader, 1000, true); @@ -632,7 +632,7 @@ BlockInfo constructBlock(mObject& _o) void updatePoW(BlockInfo& _bi) { ProofOfWork pow; - std::pair ret; + std::pair ret; while (!ProofOfWork::verify(_bi)) { ret = pow.mine(_bi, 10000, true); From 0b665270b6d7cb8f8b75eb2a3b1d8d80715ca01f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 11 Apr 2015 15:55:01 +0200 Subject: [PATCH 19/34] Add more files. --- libethash-cl/CMakeLists.txt | 47 + libethash-cl/bin2h.cmake | 86 + libethash-cl/cl.hpp | 4014 ++++++++++++++++++++++++ libethash-cl/ethash_cl_miner.cpp | 332 ++ libethash-cl/ethash_cl_miner.h | 43 + libethash-cl/ethash_cl_miner_kernel.cl | 460 +++ 6 files changed, 4982 insertions(+) create mode 100644 libethash-cl/CMakeLists.txt create mode 100644 libethash-cl/bin2h.cmake create mode 100644 libethash-cl/cl.hpp create mode 100644 libethash-cl/ethash_cl_miner.cpp create mode 100644 libethash-cl/ethash_cl_miner.h create mode 100644 libethash-cl/ethash_cl_miner_kernel.cl diff --git a/libethash-cl/CMakeLists.txt b/libethash-cl/CMakeLists.txt new file mode 100644 index 000000000..7b00a22bd --- /dev/null +++ b/libethash-cl/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.8) + +set(LIBRARY ethash-cl) +#set(CMAKE_BUILD_TYPE Release) + +include(bin2h.cmake) +bin2h(SOURCE_FILE ethash_cl_miner_kernel.cl + VARIABLE_NAME ethash_cl_miner_kernel + HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/ethash_cl_miner_kernel.h) + +if (NOT MSVC) + # Initialize CXXFLAGS for c++11 + set(CMAKE_CXX_FLAGS "-Wall -std=c++11") + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "-O4 -DNDEBUG") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") + + # Compiler-specific C++11 activation. + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (NOT (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)) + message(FATAL_ERROR "${PROJECT_NAME} requires g++ 4.7 or greater.") + endif () + elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + else () + message(FATAL_ERROR "Your C++ compiler does not support C++11.") + endif () +endif() + +set(OpenCL_FOUND TRUE) +set(OpenCL_INCLUDE_DIRS /usr/include/CL) +set(OpenCL_LIBRARIES -lOpenCL) + +if (NOT OpenCL_FOUND) + find_package(OpenCL) +endif() + +if (OpenCL_FOUND) + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -Werror -pedantic -fPIC ${CMAKE_CXX_FLAGS}") + include_directories(${OpenCL_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) + include_directories(..) + add_library(${LIBRARY} ethash_cl_miner.cpp ethash_cl_miner.h cl.hpp) + TARGET_LINK_LIBRARIES(${LIBRARY} ${OpenCL_LIBRARIES} ethash) +endif() diff --git a/libethash-cl/bin2h.cmake b/libethash-cl/bin2h.cmake new file mode 100644 index 000000000..90ca9cc5b --- /dev/null +++ b/libethash-cl/bin2h.cmake @@ -0,0 +1,86 @@ +# https://gist.github.com/sivachandran/3a0de157dccef822a230 +include(CMakeParseArguments) + +# Function to wrap a given string into multiple lines at the given column position. +# Parameters: +# VARIABLE - The name of the CMake variable holding the string. +# AT_COLUMN - The column position at which string will be wrapped. +function(WRAP_STRING) + set(oneValueArgs VARIABLE AT_COLUMN) + cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN}) + + string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength) + math(EXPR offset "0") + + while(stringLength GREATER 0) + + if(stringLength GREATER ${WRAP_STRING_AT_COLUMN}) + math(EXPR length "${WRAP_STRING_AT_COLUMN}") + else() + math(EXPR length "${stringLength}") + endif() + + string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line) + set(lines "${lines}\n${line}") + + math(EXPR stringLength "${stringLength} - ${length}") + math(EXPR offset "${offset} + ${length}") + endwhile() + + set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE) +endfunction() + +# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file +# will contain a byte array and integer variable holding the size of the array. +# Parameters +# SOURCE_FILE - The path of source file whose contents will be embedded in the header file. +# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append +# to this name and will be used a variable name for size variable. +# HEADER_FILE - The path of header file. +# APPEND - If specified appends to the header file instead of overwriting it +# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be +# useful if the source file is a text file and we want to use the file contents +# as string. But the size variable holds size of the byte array without this +# null byte. +# Usage: +# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG") +function(BIN2H) + set(options APPEND NULL_TERMINATE) + set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE) + cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN}) + + # reads source file contents as hex string + file(READ ${BIN2H_SOURCE_FILE} hexString HEX) + string(LENGTH ${hexString} hexStringLength) + + # appends null byte if asked + if(BIN2H_NULL_TERMINATE) + set(hexString "${hexString}00") + endif() + + # wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line) + wrap_string(VARIABLE hexString AT_COLUMN 32) + math(EXPR arraySize "${hexStringLength} / 2") + + # adds '0x' prefix and comma suffix before and after every byte respectively + string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString}) + # removes trailing comma + string(REGEX REPLACE ", $" "" arrayValues ${arrayValues}) + + # converts the variable name into proper C identifier + IF (${CMAKE_VERSION} GREATER 2.8.10) # fix for legacy cmake + string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) + ENDIF() + string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) + + # declares byte array and the length variables + set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };") + set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};") + + set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n") + if(BIN2H_APPEND) + file(APPEND ${BIN2H_HEADER_FILE} "${declarations}") + else() + file(WRITE ${BIN2H_HEADER_FILE} "${declarations}") + endif() +endfunction() diff --git a/libethash-cl/cl.hpp b/libethash-cl/cl.hpp new file mode 100644 index 000000000..a38498762 --- /dev/null +++ b/libethash-cl/cl.hpp @@ -0,0 +1,4014 @@ +/******************************************************************************* + * Copyright (c) 2008-2010 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/*! \file + * + * \brief C++ bindings for OpenCL 1.0 (rev 48) and OpenCL 1.1 (rev 33) + * \author Benedict R. Gaster and Laurent Morichetti + * + * Additions and fixes from Brian Cole, March 3rd 2010. + * + * \version 1.1 + * \date June 2010 + * + * Optional extension support + * + * cl + * cl_ext_device_fission + * #define USE_CL_DEVICE_FISSION + */ + +/*! \mainpage + * \section intro Introduction + * For many large applications C++ is the language of choice and so it seems + * reasonable to define C++ bindings for OpenCL. + * + * + * The interface is contained with a single C++ header file \em cl.hpp and all + * definitions are contained within the namespace \em cl. There is no additional + * requirement to include \em cl.h and to use either the C++ or original C + * bindings it is enough to simply include \em cl.hpp. + * + * The bindings themselves are lightweight and correspond closely to the + * underlying C API. Using the C++ bindings introduces no additional execution + * overhead. + * + * For detail documentation on the bindings see: + * + * The OpenCL C++ Wrapper API 1.1 (revision 04) + * http://www.khronos.org/registry/cl/specs/opencl-cplusplus-1.1.pdf + * + * \section example Example + * + * The following example shows a general use case for the C++ + * bindings, including support for the optional exception feature and + * also the supplied vector and string classes, see following sections for + * decriptions of these features. + * + * \code + * #define __CL_ENABLE_EXCEPTIONS + * + * #if defined(__APPLE__) || defined(__MACOSX) + * #include + * #else + * #include + * #endif + * #include + * #include + * #include + * + * const char * helloStr = "__kernel void " + * "hello(void) " + * "{ " + * " " + * "} "; + * + * int + * main(void) + * { + * cl_int err = CL_SUCCESS; + * try { + * + * std::vector platforms; + * cl::Platform::get(&platforms); + * if (platforms.size() == 0) { + * std::cout << "Platform size 0\n"; + * return -1; + * } + * + * cl_context_properties properties[] = + * { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0}; + * cl::Context context(CL_DEVICE_TYPE_CPU, properties); + * + * std::vector devices = context.getInfo(); + * + * cl::Program::Sources source(1, + * std::make_pair(helloStr,strlen(helloStr))); + * cl::Program program_ = cl::Program(context, source); + * program_.build(devices); + * + * cl::Kernel kernel(program_, "hello", &err); + * + * cl::Event event; + * cl::CommandQueue queue(context, devices[0], 0, &err); + * queue.enqueueNDRangeKernel( + * kernel, + * cl::NullRange, + * cl::NDRange(4,4), + * cl::NullRange, + * NULL, + * &event); + * + * event.wait(); + * } + * catch (cl::Error err) { + * std::cerr + * << "ERROR: " + * << err.what() + * << "(" + * << err.err() + * << ")" + * << std::endl; + * } + * + * return EXIT_SUCCESS; + * } + * + * \endcode + * + */ +#ifndef CL_HPP_ +#define CL_HPP_ + +#ifdef _WIN32 +#include +#include +#if defined(USE_DX_INTEROP) +#include +#endif +#endif // _WIN32 + +// +#if defined(USE_CL_DEVICE_FISSION) +#include +#endif + +#if defined(__APPLE__) || defined(__MACOSX) +#include +#include +#else +#include +#include +#endif // !__APPLE__ + +#if !defined(CL_CALLBACK) +#define CL_CALLBACK +#endif //CL_CALLBACK + +#include + +#if !defined(__NO_STD_VECTOR) +#include +#endif + +#if !defined(__NO_STD_STRING) +#include +#endif + +#if defined(linux) || defined(__APPLE__) || defined(__MACOSX) +# include +#endif // linux + +#include + +/*! \namespace cl + * + * \brief The OpenCL C++ bindings are defined within this namespace. + * + */ +namespace cl { + +#define __INIT_CL_EXT_FCN_PTR(name) \ + if(!pfn_##name) { \ + pfn_##name = (PFN_##name) \ + clGetExtensionFunctionAddress(#name); \ + if(!pfn_##name) { \ + } \ + } + +class Program; +class Device; +class Context; +class CommandQueue; +class Memory; + +#if defined(__CL_ENABLE_EXCEPTIONS) +#include +/*! \class Error + * \brief Exception class + */ +class Error : public std::exception +{ +private: + cl_int err_; + const char * errStr_; +public: + /*! Create a new CL error exception for a given error code + * and corresponding message. + */ + Error(cl_int err, const char * errStr = NULL) : err_(err), errStr_(errStr) + {} + + ~Error() throw() {} + + /*! \brief Get error string associated with exception + * + * \return A memory pointer to the error message string. + */ + virtual const char * what() const throw () + { + if (errStr_ == NULL) { + return "empty"; + } + else { + return errStr_; + } + } + + /*! \brief Get error code associated with exception + * + * \return The error code. + */ + cl_int err(void) const { return err_; } +}; + +#define __ERR_STR(x) #x +#else +#define __ERR_STR(x) NULL +#endif // __CL_ENABLE_EXCEPTIONS + +//! \cond DOXYGEN_DETAIL +#if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) +#define __GET_DEVICE_INFO_ERR __ERR_STR(clgetDeviceInfo) +#define __GET_PLATFORM_INFO_ERR __ERR_STR(clGetPlatformInfo) +#define __GET_DEVICE_IDS_ERR __ERR_STR(clGetDeviceIDs) +#define __GET_PLATFORM_IDS_ERR __ERR_STR(clGetPlatformIDs) +#define __GET_CONTEXT_INFO_ERR __ERR_STR(clGetContextInfo) +#define __GET_EVENT_INFO_ERR __ERR_STR(clGetEventInfo) +#define __GET_EVENT_PROFILE_INFO_ERR __ERR_STR(clGetEventProfileInfo) +#define __GET_MEM_OBJECT_INFO_ERR __ERR_STR(clGetMemObjectInfo) +#define __GET_IMAGE_INFO_ERR __ERR_STR(clGetImageInfo) +#define __GET_SAMPLER_INFO_ERR __ERR_STR(clGetSamplerInfo) +#define __GET_KERNEL_INFO_ERR __ERR_STR(clGetKernelInfo) +#define __GET_KERNEL_WORK_GROUP_INFO_ERR __ERR_STR(clGetKernelWorkGroupInfo) +#define __GET_PROGRAM_INFO_ERR __ERR_STR(clGetProgramInfo) +#define __GET_PROGRAM_BUILD_INFO_ERR __ERR_STR(clGetProgramBuildInfo) +#define __GET_COMMAND_QUEUE_INFO_ERR __ERR_STR(clGetCommandQueueInfo) + +#define __CREATE_CONTEXT_FROM_TYPE_ERR __ERR_STR(clCreateContextFromType) +#define __GET_SUPPORTED_IMAGE_FORMATS_ERR __ERR_STR(clGetSupportedImageFormats) + +#define __CREATE_BUFFER_ERR __ERR_STR(clCreateBuffer) +#define __CREATE_SUBBUFFER_ERR __ERR_STR(clCreateSubBuffer) +#define __CREATE_GL_BUFFER_ERR __ERR_STR(clCreateFromGLBuffer) +#define __GET_GL_OBJECT_INFO_ERR __ERR_STR(clGetGLObjectInfo) +#define __CREATE_IMAGE2D_ERR __ERR_STR(clCreateImage2D) +#define __CREATE_IMAGE3D_ERR __ERR_STR(clCreateImage3D) +#define __CREATE_SAMPLER_ERR __ERR_STR(clCreateSampler) +#define __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR __ERR_STR(clSetMemObjectDestructorCallback) + +#define __CREATE_USER_EVENT_ERR __ERR_STR(clCreateUserEvent) +#define __SET_USER_EVENT_STATUS_ERR __ERR_STR(clSetUserEventStatus) +#define __SET_EVENT_CALLBACK_ERR __ERR_STR(clSetEventCallback) +#define __WAIT_FOR_EVENTS_ERR __ERR_STR(clWaitForEvents) + +#define __CREATE_KERNEL_ERR __ERR_STR(clCreateKernel) +#define __SET_KERNEL_ARGS_ERR __ERR_STR(clSetKernelArg) +#define __CREATE_PROGRAM_WITH_SOURCE_ERR __ERR_STR(clCreateProgramWithSource) +#define __CREATE_PROGRAM_WITH_BINARY_ERR __ERR_STR(clCreateProgramWithBinary) +#define __BUILD_PROGRAM_ERR __ERR_STR(clBuildProgram) +#define __CREATE_KERNELS_IN_PROGRAM_ERR __ERR_STR(clCreateKernelsInProgram) + +#define __CREATE_COMMAND_QUEUE_ERR __ERR_STR(clCreateCommandQueue) +#define __SET_COMMAND_QUEUE_PROPERTY_ERR __ERR_STR(clSetCommandQueueProperty) +#define __ENQUEUE_READ_BUFFER_ERR __ERR_STR(clEnqueueReadBuffer) +#define __ENQUEUE_READ_BUFFER_RECT_ERR __ERR_STR(clEnqueueReadBufferRect) +#define __ENQUEUE_WRITE_BUFFER_ERR __ERR_STR(clEnqueueWriteBuffer) +#define __ENQUEUE_WRITE_BUFFER_RECT_ERR __ERR_STR(clEnqueueWriteBufferRect) +#define __ENQEUE_COPY_BUFFER_ERR __ERR_STR(clEnqueueCopyBuffer) +#define __ENQEUE_COPY_BUFFER_RECT_ERR __ERR_STR(clEnqueueCopyBufferRect) +#define __ENQUEUE_READ_IMAGE_ERR __ERR_STR(clEnqueueReadImage) +#define __ENQUEUE_WRITE_IMAGE_ERR __ERR_STR(clEnqueueWriteImage) +#define __ENQUEUE_COPY_IMAGE_ERR __ERR_STR(clEnqueueCopyImage) +#define __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR __ERR_STR(clEnqueueCopyImageToBuffer) +#define __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR __ERR_STR(clEnqueueCopyBufferToImage) +#define __ENQUEUE_MAP_BUFFER_ERR __ERR_STR(clEnqueueMapBuffer) +#define __ENQUEUE_MAP_IMAGE_ERR __ERR_STR(clEnqueueMapImage) +#define __ENQUEUE_UNMAP_MEM_OBJECT_ERR __ERR_STR(clEnqueueUnMapMemObject) +#define __ENQUEUE_NDRANGE_KERNEL_ERR __ERR_STR(clEnqueueNDRangeKernel) +#define __ENQUEUE_TASK_ERR __ERR_STR(clEnqueueTask) +#define __ENQUEUE_NATIVE_KERNEL __ERR_STR(clEnqueueNativeKernel) +#define __ENQUEUE_MARKER_ERR __ERR_STR(clEnqueueMarker) +#define __ENQUEUE_WAIT_FOR_EVENTS_ERR __ERR_STR(clEnqueueWaitForEvents) +#define __ENQUEUE_BARRIER_ERR __ERR_STR(clEnqueueBarrier) + +#define __ENQUEUE_ACQUIRE_GL_ERR __ERR_STR(clEnqueueAcquireGLObjects) +#define __ENQUEUE_RELEASE_GL_ERR __ERR_STR(clEnqueueReleaseGLObjects) + +#define __UNLOAD_COMPILER_ERR __ERR_STR(clUnloadCompiler) + +#define __FLUSH_ERR __ERR_STR(clFlush) +#define __FINISH_ERR __ERR_STR(clFinish) + +#define __CREATE_SUB_DEVICES __ERR_STR(clCreateSubDevicesEXT) +#endif // __CL_USER_OVERRIDE_ERROR_STRINGS +//! \endcond + +/*! \class string + * \brief Simple string class, that provides a limited subset of std::string + * functionality but avoids many of the issues that come with that class. + */ +class string +{ +private: + ::size_t size_; + char * str_; +public: + string(void) : size_(0), str_(NULL) + { + } + + string(char * str, ::size_t size) : + size_(size), + str_(NULL) + { + str_ = new char[size_+1]; + if (str_ != NULL) { + memcpy(str_, str, size_ * sizeof(char)); + str_[size_] = '\0'; + } + else { + size_ = 0; + } + } + + string(char * str) : + str_(NULL) + { + size_= ::strlen(str); + str_ = new char[size_ + 1]; + if (str_ != NULL) { + memcpy(str_, str, (size_ + 1) * sizeof(char)); + } + else { + size_ = 0; + } + } + + string& operator=(const string& rhs) + { + if (this == &rhs) { + return *this; + } + + if (rhs.size_ == 0 || rhs.str_ == NULL) { + size_ = 0; + str_ = NULL; + } + else { + size_ = rhs.size_; + str_ = new char[size_ + 1]; + if (str_ != NULL) { + memcpy(str_, rhs.str_, (size_ + 1) * sizeof(char)); + } + else { + size_ = 0; + } + } + + return *this; + } + + string(const string& rhs) + { + *this = rhs; + } + + ~string() + { + if (str_ != NULL) { + delete[] str_; + } + } + + ::size_t size(void) const { return size_; } + ::size_t length(void) const { return size(); } + + const char * c_str(void) const { return (str_) ? str_ : "";} +}; + +#if !defined(__USE_DEV_STRING) && !defined(__NO_STD_STRING) +#include +typedef std::string STRING_CLASS; +#elif !defined(__USE_DEV_STRING) +typedef cl::string STRING_CLASS; +#endif + +#if !defined(__USE_DEV_VECTOR) && !defined(__NO_STD_VECTOR) +#include +#define VECTOR_CLASS std::vector +#elif !defined(__USE_DEV_VECTOR) +#define VECTOR_CLASS cl::vector +#endif + +#if !defined(__MAX_DEFAULT_VECTOR_SIZE) +#define __MAX_DEFAULT_VECTOR_SIZE 10 +#endif + +/*! \class vector + * \brief Fixed sized vector implementation that mirroring + * std::vector functionality. + */ +template +class vector +{ +private: + T data_[N]; + unsigned int size_; + bool empty_; +public: + vector() : + size_(-1), + empty_(true) + {} + + ~vector() {} + + unsigned int size(void) const + { + return size_ + 1; + } + + void clear() + { + size_ = -1; + empty_ = true; + } + + void push_back (const T& x) + { + if (size() < N) { + size_++; + data_[size_] = x; + empty_ = false; + } + } + + void pop_back(void) + { + if (!empty_) { + data_[size_].~T(); + size_--; + if (size_ == -1) { + empty_ = true; + } + } + } + + vector(const vector& vec) : + size_(vec.size_), + empty_(vec.empty_) + { + if (!empty_) { + memcpy(&data_[0], &vec.data_[0], size() * sizeof(T)); + } + } + + vector(unsigned int size, const T& val = T()) : + size_(-1), + empty_(true) + { + for (unsigned int i = 0; i < size; i++) { + push_back(val); + } + } + + vector& operator=(const vector& rhs) + { + if (this == &rhs) { + return *this; + } + + size_ = rhs.size_; + empty_ = rhs.empty_; + + if (!empty_) { + memcpy(&data_[0], &rhs.data_[0], size() * sizeof(T)); + } + + return *this; + } + + bool operator==(vector &vec) + { + if (empty_ && vec.empty_) { + return true; + } + + if (size() != vec.size()) { + return false; + } + + return memcmp(&data_[0], &vec.data_[0], size() * sizeof(T)) == 0 ? true : false; + } + + operator T* () { return data_; } + operator const T* () const { return data_; } + + bool empty (void) const + { + return empty_; + } + + unsigned int max_size (void) const + { + return N; + } + + unsigned int capacity () const + { + return sizeof(T) * N; + } + + T& operator[](int index) + { + return data_[index]; + } + + T operator[](int index) const + { + return data_[index]; + } + + template + void assign(I start, I end) + { + clear(); + while(start < end) { + push_back(*start); + start++; + } + } + + /*! \class iterator + * \brief Iterator class for vectors + */ + class iterator + { + private: + vector vec_; + int index_; + bool initialized_; + public: + iterator(void) : + index_(-1), + initialized_(false) + { + index_ = -1; + initialized_ = false; + } + + ~iterator(void) {} + + static iterator begin(vector &vec) + { + iterator i; + + if (!vec.empty()) { + i.index_ = 0; + } + + i.vec_ = vec; + i.initialized_ = true; + return i; + } + + static iterator end(vector &vec) + { + iterator i; + + if (!vec.empty()) { + i.index_ = vec.size(); + } + i.vec_ = vec; + i.initialized_ = true; + return i; + } + + bool operator==(iterator i) + { + return ((vec_ == i.vec_) && + (index_ == i.index_) && + (initialized_ == i.initialized_)); + } + + bool operator!=(iterator i) + { + return (!(*this==i)); + } + + void operator++() + { + index_++; + } + + void operator++(int x) + { + index_ += x; + } + + void operator--() + { + index_--; + } + + void operator--(int x) + { + index_ -= x; + } + + T operator *() + { + return vec_[index_]; + } + }; + + iterator begin(void) + { + return iterator::begin(*this); + } + + iterator end(void) + { + return iterator::end(*this); + } + + T& front(void) + { + return data_[0]; + } + + T& back(void) + { + return data_[size_]; + } + + const T& front(void) const + { + return data_[0]; + } + + const T& back(void) const + { + return data_[size_]; + } +}; + +/*! + * \brief size_t class used to interface between C++ and + * OpenCL C calls that require arrays of size_t values, who's + * size is known statically. + */ +template +struct size_t : public cl::vector< ::size_t, N> { }; + +namespace detail { + +// GetInfo help struct +template +struct GetInfoHelper +{ + static cl_int + get(Functor f, cl_uint name, T* param) + { + return f(name, sizeof(T), param, NULL); + } +}; + +// Specialized GetInfoHelper for VECTOR_CLASS params +template +struct GetInfoHelper > +{ + static cl_int get(Func f, cl_uint name, VECTOR_CLASS* param) + { + ::size_t required; + cl_int err = f(name, 0, NULL, &required); + if (err != CL_SUCCESS) { + return err; + } + + T* value = (T*) alloca(required); + err = f(name, required, value, NULL); + if (err != CL_SUCCESS) { + return err; + } + + param->assign(&value[0], &value[required/sizeof(T)]); + return CL_SUCCESS; + } +}; + +// Specialized for getInfo +template +struct GetInfoHelper > +{ + static cl_int + get(Func f, cl_uint name, VECTOR_CLASS* param) + { + cl_uint err = f(name, param->size() * sizeof(char *), &(*param)[0], NULL); + if (err != CL_SUCCESS) { + return err; + } + + return CL_SUCCESS; + } +}; + +// Specialized GetInfoHelper for STRING_CLASS params +template +struct GetInfoHelper +{ + static cl_int get(Func f, cl_uint name, STRING_CLASS* param) + { + ::size_t required; + cl_int err = f(name, 0, NULL, &required); + if (err != CL_SUCCESS) { + return err; + } + + char* value = (char*) alloca(required); + err = f(name, required, value, NULL); + if (err != CL_SUCCESS) { + return err; + } + + *param = value; + return CL_SUCCESS; + } +}; + +#define __GET_INFO_HELPER_WITH_RETAIN(CPP_TYPE) \ +namespace detail { \ +template \ +struct GetInfoHelper \ +{ \ + static cl_int get(Func f, cl_uint name, CPP_TYPE* param) \ + { \ + cl_uint err = f(name, sizeof(CPP_TYPE), param, NULL); \ + if (err != CL_SUCCESS) { \ + return err; \ + } \ + \ + return ReferenceHandler::retain((*param)()); \ + } \ +}; \ +} + + +#define __PARAM_NAME_INFO_1_0(F) \ + F(cl_platform_info, CL_PLATFORM_PROFILE, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_VERSION, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_NAME, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_VENDOR, STRING_CLASS) \ + F(cl_platform_info, CL_PLATFORM_EXTENSIONS, STRING_CLASS) \ + \ + F(cl_device_info, CL_DEVICE_TYPE, cl_device_type) \ + F(cl_device_info, CL_DEVICE_VENDOR_ID, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_GROUP_SIZE, ::size_t) \ + F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_SIZES, VECTOR_CLASS< ::size_t>) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint) \ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint) \ + F(cl_device_info, CL_DEVICE_ADDRESS_BITS, cl_bitfield) \ + F(cl_device_info, CL_DEVICE_MAX_READ_IMAGE_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_WIDTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_HEIGHT, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_WIDTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_HEIGHT, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_DEPTH, ::size_t) \ + F(cl_device_info, CL_DEVICE_IMAGE_SUPPORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_MAX_PARAMETER_SIZE, ::size_t) \ + F(cl_device_info, CL_DEVICE_MAX_SAMPLERS, cl_uint) \ + F(cl_device_info, CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint) \ + F(cl_device_info, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, cl_uint) \ + F(cl_device_info, CL_DEVICE_SINGLE_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, cl_device_mem_cache_type) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint)\ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_MAX_CONSTANT_ARGS, cl_uint) \ + F(cl_device_info, CL_DEVICE_LOCAL_MEM_TYPE, cl_device_local_mem_type) \ + F(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong) \ + F(cl_device_info, CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool) \ + F(cl_device_info, CL_DEVICE_PROFILING_TIMER_RESOLUTION, ::size_t) \ + F(cl_device_info, CL_DEVICE_ENDIAN_LITTLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_AVAILABLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_COMPILER_AVAILABLE, cl_bool) \ + F(cl_device_info, CL_DEVICE_EXECUTION_CAPABILITIES, cl_device_exec_capabilities) \ + F(cl_device_info, CL_DEVICE_QUEUE_PROPERTIES, cl_command_queue_properties) \ + F(cl_device_info, CL_DEVICE_PLATFORM, cl_platform_id) \ + F(cl_device_info, CL_DEVICE_NAME, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_VENDOR, STRING_CLASS) \ + F(cl_device_info, CL_DRIVER_VERSION, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_PROFILE, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_VERSION, STRING_CLASS) \ + F(cl_device_info, CL_DEVICE_EXTENSIONS, STRING_CLASS) \ + \ + F(cl_context_info, CL_CONTEXT_REFERENCE_COUNT, cl_uint) \ + F(cl_context_info, CL_CONTEXT_DEVICES, VECTOR_CLASS) \ + F(cl_context_info, CL_CONTEXT_PROPERTIES, VECTOR_CLASS) \ + \ + F(cl_event_info, CL_EVENT_COMMAND_QUEUE, cl::CommandQueue) \ + F(cl_event_info, CL_EVENT_COMMAND_TYPE, cl_command_type) \ + F(cl_event_info, CL_EVENT_REFERENCE_COUNT, cl_uint) \ + F(cl_event_info, CL_EVENT_COMMAND_EXECUTION_STATUS, cl_uint) \ + \ + F(cl_profiling_info, CL_PROFILING_COMMAND_QUEUED, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_SUBMIT, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_START, cl_ulong) \ + F(cl_profiling_info, CL_PROFILING_COMMAND_END, cl_ulong) \ + \ + F(cl_mem_info, CL_MEM_TYPE, cl_mem_object_type) \ + F(cl_mem_info, CL_MEM_FLAGS, cl_mem_flags) \ + F(cl_mem_info, CL_MEM_SIZE, ::size_t) \ + F(cl_mem_info, CL_MEM_HOST_PTR, void*) \ + F(cl_mem_info, CL_MEM_MAP_COUNT, cl_uint) \ + F(cl_mem_info, CL_MEM_REFERENCE_COUNT, cl_uint) \ + F(cl_mem_info, CL_MEM_CONTEXT, cl::Context) \ + \ + F(cl_image_info, CL_IMAGE_FORMAT, cl_image_format) \ + F(cl_image_info, CL_IMAGE_ELEMENT_SIZE, ::size_t) \ + F(cl_image_info, CL_IMAGE_ROW_PITCH, ::size_t) \ + F(cl_image_info, CL_IMAGE_SLICE_PITCH, ::size_t) \ + F(cl_image_info, CL_IMAGE_WIDTH, ::size_t) \ + F(cl_image_info, CL_IMAGE_HEIGHT, ::size_t) \ + F(cl_image_info, CL_IMAGE_DEPTH, ::size_t) \ + \ + F(cl_sampler_info, CL_SAMPLER_REFERENCE_COUNT, cl_uint) \ + F(cl_sampler_info, CL_SAMPLER_CONTEXT, cl::Context) \ + F(cl_sampler_info, CL_SAMPLER_NORMALIZED_COORDS, cl_addressing_mode) \ + F(cl_sampler_info, CL_SAMPLER_ADDRESSING_MODE, cl_filter_mode) \ + F(cl_sampler_info, CL_SAMPLER_FILTER_MODE, cl_bool) \ + \ + F(cl_program_info, CL_PROGRAM_REFERENCE_COUNT, cl_uint) \ + F(cl_program_info, CL_PROGRAM_CONTEXT, cl::Context) \ + F(cl_program_info, CL_PROGRAM_NUM_DEVICES, cl_uint) \ + F(cl_program_info, CL_PROGRAM_DEVICES, VECTOR_CLASS) \ + F(cl_program_info, CL_PROGRAM_SOURCE, STRING_CLASS) \ + F(cl_program_info, CL_PROGRAM_BINARY_SIZES, VECTOR_CLASS< ::size_t>) \ + F(cl_program_info, CL_PROGRAM_BINARIES, VECTOR_CLASS) \ + \ + F(cl_program_build_info, CL_PROGRAM_BUILD_STATUS, cl_build_status) \ + F(cl_program_build_info, CL_PROGRAM_BUILD_OPTIONS, STRING_CLASS) \ + F(cl_program_build_info, CL_PROGRAM_BUILD_LOG, STRING_CLASS) \ + \ + F(cl_kernel_info, CL_KERNEL_FUNCTION_NAME, STRING_CLASS) \ + F(cl_kernel_info, CL_KERNEL_NUM_ARGS, cl_uint) \ + F(cl_kernel_info, CL_KERNEL_REFERENCE_COUNT, cl_uint) \ + F(cl_kernel_info, CL_KERNEL_CONTEXT, cl::Context) \ + F(cl_kernel_info, CL_KERNEL_PROGRAM, cl::Program) \ + \ + F(cl_kernel_work_group_info, CL_KERNEL_WORK_GROUP_SIZE, ::size_t) \ + F(cl_kernel_work_group_info, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, cl::size_t<3>) \ + F(cl_kernel_work_group_info, CL_KERNEL_LOCAL_MEM_SIZE, cl_ulong) \ + \ + F(cl_command_queue_info, CL_QUEUE_CONTEXT, cl::Context) \ + F(cl_command_queue_info, CL_QUEUE_DEVICE, cl::Device) \ + F(cl_command_queue_info, CL_QUEUE_REFERENCE_COUNT, cl_uint) \ + F(cl_command_queue_info, CL_QUEUE_PROPERTIES, cl_command_queue_properties) + +#if defined(CL_VERSION_1_1) +#define __PARAM_NAME_INFO_1_1(F) \ + F(cl_context_info, CL_CONTEXT_NUM_DEVICES, cl_uint)\ + F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint) \ + F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, cl_uint) \ + F(cl_device_info, CL_DEVICE_DOUBLE_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_HALF_FP_CONFIG, cl_device_fp_config) \ + F(cl_device_info, CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool) \ + \ + F(cl_mem_info, CL_MEM_ASSOCIATED_MEMOBJECT, cl::Memory) \ + F(cl_mem_info, CL_MEM_OFFSET, ::size_t) \ + \ + F(cl_kernel_work_group_info, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, ::size_t) \ + F(cl_kernel_work_group_info, CL_KERNEL_PRIVATE_MEM_SIZE, cl_ulong) \ + \ + F(cl_event_info, CL_EVENT_CONTEXT, cl::Context) +#endif // CL_VERSION_1_1 + +#if defined(USE_CL_DEVICE_FISSION) +#define __PARAM_NAME_DEVICE_FISSION(F) \ + F(cl_device_info, CL_DEVICE_PARENT_DEVICE_EXT, cl_device_id) \ + F(cl_device_info, CL_DEVICE_PARTITION_TYPES_EXT, VECTOR_CLASS) \ + F(cl_device_info, CL_DEVICE_AFFINITY_DOMAINS_EXT, VECTOR_CLASS) \ + F(cl_device_info, CL_DEVICE_REFERENCE_COUNT_EXT , cl_uint) \ + F(cl_device_info, CL_DEVICE_PARTITION_STYLE_EXT, VECTOR_CLASS) +#endif // USE_CL_DEVICE_FISSION + +template +struct param_traits {}; + +#define __DECLARE_PARAM_TRAITS(token, param_name, T) \ +struct token; \ +template<> \ +struct param_traits \ +{ \ + enum { value = param_name }; \ + typedef T param_type; \ +}; + +__PARAM_NAME_INFO_1_0(__DECLARE_PARAM_TRAITS) +#if defined(CL_VERSION_1_1) +__PARAM_NAME_INFO_1_1(__DECLARE_PARAM_TRAITS) +#endif // CL_VERSION_1_1 + +#if defined(USE_CL_DEVICE_FISSION) +__PARAM_NAME_DEVICE_FISSION(__DECLARE_PARAM_TRAITS); +#endif // USE_CL_DEVICE_FISSION + +#undef __DECLARE_PARAM_TRAITS + +// Convenience functions + +template +inline cl_int +getInfo(Func f, cl_uint name, T* param) +{ + return GetInfoHelper::get(f, name, param); +} + +template +struct GetInfoFunctor0 +{ + Func f_; const Arg0& arg0_; + cl_int operator ()( + cl_uint param, ::size_t size, void* value, ::size_t* size_ret) + { return f_(arg0_, param, size, value, size_ret); } +}; + +template +struct GetInfoFunctor1 +{ + Func f_; const Arg0& arg0_; const Arg1& arg1_; + cl_int operator ()( + cl_uint param, ::size_t size, void* value, ::size_t* size_ret) + { return f_(arg0_, arg1_, param, size, value, size_ret); } +}; + +template +inline cl_int +getInfo(Func f, const Arg0& arg0, cl_uint name, T* param) +{ + GetInfoFunctor0 f0 = { f, arg0 }; + return GetInfoHelper, T> + ::get(f0, name, param); +} + +template +inline cl_int +getInfo(Func f, const Arg0& arg0, const Arg1& arg1, cl_uint name, T* param) +{ + GetInfoFunctor1 f0 = { f, arg0, arg1 }; + return GetInfoHelper, T> + ::get(f0, name, param); +} + +template +struct ReferenceHandler +{ }; + +template <> +struct ReferenceHandler +{ + // cl_device_id does not have retain(). + static cl_int retain(cl_device_id) + { return CL_INVALID_DEVICE; } + // cl_device_id does not have release(). + static cl_int release(cl_device_id) + { return CL_INVALID_DEVICE; } +}; + +template <> +struct ReferenceHandler +{ + // cl_platform_id does not have retain(). + static cl_int retain(cl_platform_id) + { return CL_INVALID_PLATFORM; } + // cl_platform_id does not have release(). + static cl_int release(cl_platform_id) + { return CL_INVALID_PLATFORM; } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_context context) + { return ::clRetainContext(context); } + static cl_int release(cl_context context) + { return ::clReleaseContext(context); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_command_queue queue) + { return ::clRetainCommandQueue(queue); } + static cl_int release(cl_command_queue queue) + { return ::clReleaseCommandQueue(queue); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_mem memory) + { return ::clRetainMemObject(memory); } + static cl_int release(cl_mem memory) + { return ::clReleaseMemObject(memory); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_sampler sampler) + { return ::clRetainSampler(sampler); } + static cl_int release(cl_sampler sampler) + { return ::clReleaseSampler(sampler); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_program program) + { return ::clRetainProgram(program); } + static cl_int release(cl_program program) + { return ::clReleaseProgram(program); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_kernel kernel) + { return ::clRetainKernel(kernel); } + static cl_int release(cl_kernel kernel) + { return ::clReleaseKernel(kernel); } +}; + +template <> +struct ReferenceHandler +{ + static cl_int retain(cl_event event) + { return ::clRetainEvent(event); } + static cl_int release(cl_event event) + { return ::clReleaseEvent(event); } +}; + +template +class Wrapper +{ +public: + typedef T cl_type; + +protected: + cl_type object_; + +public: + Wrapper() : object_(NULL) { } + + ~Wrapper() + { + if (object_ != NULL) { release(); } + } + + Wrapper(const Wrapper& rhs) + { + object_ = rhs.object_; + if (object_ != NULL) { retain(); } + } + + Wrapper& operator = (const Wrapper& rhs) + { + if (object_ != NULL) { release(); } + object_ = rhs.object_; + if (object_ != NULL) { retain(); } + return *this; + } + + cl_type operator ()() const { return object_; } + + cl_type& operator ()() { return object_; } + +protected: + + cl_int retain() const + { + return ReferenceHandler::retain(object_); + } + + cl_int release() const + { + return ReferenceHandler::release(object_); + } +}; + +#if defined(__CL_ENABLE_EXCEPTIONS) +static inline cl_int errHandler ( + cl_int err, + const char * errStr = NULL) throw(Error) +{ + if (err != CL_SUCCESS) { + throw Error(err, errStr); + } + return err; +} +#else +static inline cl_int errHandler (cl_int err, const char * errStr = NULL) +{ + return err; +} +#endif // __CL_ENABLE_EXCEPTIONS + +} // namespace detail +//! \endcond + +/*! \stuct ImageFormat + * \brief ImageFormat interface fro cl_image_format. + */ +struct ImageFormat : public cl_image_format +{ + ImageFormat(){} + + ImageFormat(cl_channel_order order, cl_channel_type type) + { + image_channel_order = order; + image_channel_data_type = type; + } + + ImageFormat& operator = (const ImageFormat& rhs) + { + if (this != &rhs) { + this->image_channel_data_type = rhs.image_channel_data_type; + this->image_channel_order = rhs.image_channel_order; + } + return *this; + } +}; + +/*! \class Device + * \brief Device interface for cl_device_id. + */ +class Device : public detail::Wrapper +{ +public: + Device(cl_device_id device) { object_ = device; } + + Device() : detail::Wrapper() { } + + Device(const Device& device) : detail::Wrapper(device) { } + + Device& operator = (const Device& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_device_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetDeviceInfo, object_, name, param), + __GET_DEVICE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_device_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + +#if defined(USE_CL_DEVICE_FISSION) + cl_int createSubDevices( + const cl_device_partition_property_ext * properties, + VECTOR_CLASS* devices) + { + typedef CL_API_ENTRY cl_int + ( CL_API_CALL * PFN_clCreateSubDevicesEXT)( + cl_device_id /*in_device*/, + const cl_device_partition_property_ext * /* properties */, + cl_uint /*num_entries*/, + cl_device_id * /*out_devices*/, + cl_uint * /*num_devices*/ ) CL_EXT_SUFFIX__VERSION_1_1; + + static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = NULL; + __INIT_CL_EXT_FCN_PTR(clCreateSubDevicesEXT); + + cl_uint n = 0; + cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_SUB_DEVICES); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_SUB_DEVICES); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +#endif +}; + +/*! \class Platform + * \brief Platform interface. + */ +class Platform : public detail::Wrapper +{ +public: + static const Platform null(); + + Platform(cl_platform_id platform) { object_ = platform; } + + Platform() : detail::Wrapper() { } + + Platform(const Platform& platform) : detail::Wrapper(platform) { } + + Platform& operator = (const Platform& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + cl_int getInfo(cl_platform_info name, STRING_CLASS* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetPlatformInfo, object_, name, param), + __GET_PLATFORM_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_platform_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int getDevices( + cl_device_type type, + VECTOR_CLASS* devices) const + { + cl_uint n = 0; + cl_int err = ::clGetDeviceIDs(object_, type, 0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = ::clGetDeviceIDs(object_, type, n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } + +#if defined(USE_DX_INTEROP) + /*! \brief Get the list of available D3D10 devices. + * + * \param d3d_device_source. + * + * \param d3d_object. + * + * \param d3d_device_set. + * + * \param devices returns a vector of OpenCL D3D10 devices found. The cl::Device + * values returned in devices can be used to identify a specific OpenCL + * device. If \a devices argument is NULL, this argument is ignored. + * + * \return One of the following values: + * - CL_SUCCESS if the function is executed successfully. + * + * The application can query specific capabilities of the OpenCL device(s) + * returned by cl::getDevices. This can be used by the application to + * determine which device(s) to use. + * + * \note In the case that exceptions are enabled and a return value + * other than CL_SUCCESS is generated, then cl::Error exception is + * generated. + */ + cl_int getDevices( + cl_d3d10_device_source_khr d3d_device_source, + void * d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + VECTOR_CLASS* devices) const + { + typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clGetDeviceIDsFromD3D10KHR)( + cl_platform_id platform, + cl_d3d10_device_source_khr d3d_device_source, + void * d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id * devices, + cl_uint* num_devices); + + static PFN_clGetDeviceIDsFromD3D10KHR pfn_clGetDeviceIDsFromD3D10KHR = NULL; + __INIT_CL_EXT_FCN_PTR(clGetDeviceIDsFromD3D10KHR); + + cl_uint n = 0; + cl_int err = pfn_clGetDeviceIDsFromD3D10KHR( + object_, + d3d_device_source, + d3d_object, + d3d_device_set, + 0, + NULL, + &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id)); + err = pfn_clGetDeviceIDsFromD3D10KHR( + object_, + d3d_device_source, + d3d_object, + d3d_device_set, + n, + ids, + NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_DEVICE_IDS_ERR); + } + + devices->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +#endif + + static cl_int get( + VECTOR_CLASS* platforms) + { + cl_uint n = 0; + cl_int err = ::clGetPlatformIDs(0, NULL, &n); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); + } + + cl_platform_id* ids = (cl_platform_id*) alloca( + n * sizeof(cl_platform_id)); + err = ::clGetPlatformIDs(n, ids, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); + } + + platforms->assign(&ids[0], &ids[n]); + return CL_SUCCESS; + } +}; + +static inline cl_int +UnloadCompiler() +{ + return ::clUnloadCompiler(); +} + +class Context : public detail::Wrapper +{ +public: + Context( + const VECTOR_CLASS& devices, + cl_context_properties* properties = NULL, + void (CL_CALLBACK * notifyFptr)( + const char *, + const void *, + ::size_t, + void *) = NULL, + void* data = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateContext( + properties, (cl_uint) devices.size(), + (cl_device_id*) &devices.front(), + notifyFptr, data, &error); + + detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); + if (err != NULL) { + *err = error; + } + } + + Context( + cl_device_type type, + cl_context_properties* properties = NULL, + void (CL_CALLBACK * notifyFptr)( + const char *, + const void *, + ::size_t, + void *) = NULL, + void* data = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateContextFromType( + properties, type, notifyFptr, data, &error); + + detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); + if (err != NULL) { + *err = error; + } + } + + Context() : detail::Wrapper() { } + + Context(const Context& context) : detail::Wrapper(context) { } + + Context& operator = (const Context& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_context_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetContextInfo, object_, name, param), + __GET_CONTEXT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_context_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int getSupportedImageFormats( + cl_mem_flags flags, + cl_mem_object_type type, + VECTOR_CLASS* formats) const + { + cl_uint numEntries; + cl_int err = ::clGetSupportedImageFormats( + object_, + flags, + type, + 0, + NULL, + &numEntries); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); + } + + ImageFormat* value = (ImageFormat*) + alloca(numEntries * sizeof(ImageFormat)); + err = ::clGetSupportedImageFormats( + object_, + flags, + type, + numEntries, + (cl_image_format*) value, + NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); + } + + formats->assign(&value[0], &value[numEntries]); + return CL_SUCCESS; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Context) + +/*! \class Event + * \brief Event interface for cl_event. + */ +class Event : public detail::Wrapper +{ +public: + Event() : detail::Wrapper() { } + + Event(const Event& event) : detail::Wrapper(event) { } + + Event& operator = (const Event& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_event_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetEventInfo, object_, name, param), + __GET_EVENT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_event_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getProfilingInfo(cl_profiling_info name, T* param) const + { + return detail::errHandler(detail::getInfo( + &::clGetEventProfilingInfo, object_, name, param), + __GET_EVENT_PROFILE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getProfilingInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_profiling_info, name>::param_type param; + cl_int result = getProfilingInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int wait() const + { + return detail::errHandler( + ::clWaitForEvents(1, &object_), + __WAIT_FOR_EVENTS_ERR); + } + +#if defined(CL_VERSION_1_1) + cl_int setCallback( + cl_int type, + void (CL_CALLBACK * pfn_notify)(cl_event, cl_int, void *), + void * user_data = NULL) + { + return detail::errHandler( + ::clSetEventCallback( + object_, + type, + pfn_notify, + user_data), + __SET_EVENT_CALLBACK_ERR); + } +#endif + + static cl_int + waitForEvents(const VECTOR_CLASS& events) + { + return detail::errHandler( + ::clWaitForEvents( + (cl_uint) events.size(), (cl_event*)&events.front()), + __WAIT_FOR_EVENTS_ERR); + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Event) + +#if defined(CL_VERSION_1_1) +/*! \class UserEvent + * \brief User event interface for cl_event. + */ +class UserEvent : public Event +{ +public: + UserEvent( + const Context& context, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateUserEvent( + context(), + &error); + + detail::errHandler(error, __CREATE_USER_EVENT_ERR); + if (err != NULL) { + *err = error; + } + } + + UserEvent() : Event() { } + + UserEvent(const UserEvent& event) : Event(event) { } + + UserEvent& operator = (const UserEvent& rhs) + { + if (this != &rhs) { + Event::operator=(rhs); + } + return *this; + } + + cl_int setStatus(cl_int status) + { + return detail::errHandler( + ::clSetUserEventStatus(object_,status), + __SET_USER_EVENT_STATUS_ERR); + } +}; +#endif + +inline static cl_int +WaitForEvents(const VECTOR_CLASS& events) +{ + return detail::errHandler( + ::clWaitForEvents( + (cl_uint) events.size(), (cl_event*)&events.front()), + __WAIT_FOR_EVENTS_ERR); +} + +/*! \class Memory + * \brief Memory interface for cl_mem. + */ +class Memory : public detail::Wrapper +{ +public: + Memory() : detail::Wrapper() { } + + Memory(const Memory& memory) : detail::Wrapper(memory) { } + + Memory& operator = (const Memory& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_mem_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetMemObjectInfo, object_, name, param), + __GET_MEM_OBJECT_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_mem_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + +#if defined(CL_VERSION_1_1) + cl_int setDestructorCallback( + void (CL_CALLBACK * pfn_notify)(cl_mem, void *), + void * user_data = NULL) + { + return detail::errHandler( + ::clSetMemObjectDestructorCallback( + object_, + pfn_notify, + user_data), + __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR); + } +#endif + +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Memory) + +/*! \class Buffer + * \brief Memory buffer interface. + */ +class Buffer : public Memory +{ +public: + Buffer( + const Context& context, + cl_mem_flags flags, + ::size_t size, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); + + detail::errHandler(error, __CREATE_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Buffer() : Memory() { } + + Buffer(const Buffer& buffer) : Memory(buffer) { } + + Buffer& operator = (const Buffer& rhs) + { + if (this != &rhs) { + Memory::operator=(rhs); + } + return *this; + } + +#if defined(CL_VERSION_1_1) + Buffer createSubBuffer( + cl_mem_flags flags, + cl_buffer_create_type buffer_create_type, + const void * buffer_create_info, + cl_int * err = NULL) + { + Buffer result; + cl_int error; + result.object_ = ::clCreateSubBuffer( + object_, + flags, + buffer_create_type, + buffer_create_info, + &error); + + detail::errHandler(error, __CREATE_SUBBUFFER_ERR); + if (err != NULL) { + *err = error; + } + + return result; + } +#endif +}; + +#if defined (USE_DX_INTEROP) +class BufferD3D10 : public Buffer +{ +public: + typedef CL_API_ENTRY cl_mem (CL_API_CALL *PFN_clCreateFromD3D10BufferKHR)( + cl_context context, cl_mem_flags flags, ID3D10Buffer* buffer, + cl_int* errcode_ret); + + BufferD3D10( + const Context& context, + cl_mem_flags flags, + ID3D10Buffer* bufobj, + cl_int * err = NULL) + { + static PFN_clCreateFromD3D10BufferKHR pfn_clCreateFromD3D10BufferKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clCreateFromD3D10BufferKHR); + + cl_int error; + object_ = pfn_clCreateFromD3D10BufferKHR( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferD3D10() : Buffer() { } + + BufferD3D10(const BufferD3D10& buffer) : Buffer(buffer) { } + + BufferD3D10& operator = (const BufferD3D10& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } +}; +#endif + +/*! \class BufferGL + * \brief Memory buffer interface for GL interop. + */ +class BufferGL : public Buffer +{ +public: + BufferGL( + const Context& context, + cl_mem_flags flags, + GLuint bufobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLBuffer( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferGL() : Buffer() { } + + BufferGL(const BufferGL& buffer) : Buffer(buffer) { } + + BufferGL& operator = (const BufferGL& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } + + cl_int getObjectInfo( + cl_gl_object_type *type, + GLuint * gl_object_name) + { + return detail::errHandler( + ::clGetGLObjectInfo(object_,type,gl_object_name), + __GET_GL_OBJECT_INFO_ERR); + } +}; + +/*! \class BufferRenderGL + * \brief Memory buffer interface for GL interop with renderbuffer. + */ +class BufferRenderGL : public Buffer +{ +public: + BufferRenderGL( + const Context& context, + cl_mem_flags flags, + GLuint bufobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLRenderbuffer( + context(), + flags, + bufobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + BufferRenderGL() : Buffer() { } + + BufferRenderGL(const BufferGL& buffer) : Buffer(buffer) { } + + BufferRenderGL& operator = (const BufferRenderGL& rhs) + { + if (this != &rhs) { + Buffer::operator=(rhs); + } + return *this; + } + + cl_int getObjectInfo( + cl_gl_object_type *type, + GLuint * gl_object_name) + { + return detail::errHandler( + ::clGetGLObjectInfo(object_,type,gl_object_name), + __GET_GL_OBJECT_INFO_ERR); + } +}; + +/*! \class Image + * \brief Base class interface for all images. + */ +class Image : public Memory +{ +protected: + Image() : Memory() { } + + Image(const Image& image) : Memory(image) { } + + Image& operator = (const Image& rhs) + { + if (this != &rhs) { + Memory::operator=(rhs); + } + return *this; + } +public: + template + cl_int getImageInfo(cl_image_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetImageInfo, object_, name, param), + __GET_IMAGE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getImageInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_image_info, name>::param_type param; + cl_int result = getImageInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } +}; + +/*! \class Image2D + * \brief Image interface for 2D images. + */ +class Image2D : public Image +{ +public: + Image2D( + const Context& context, + cl_mem_flags flags, + ImageFormat format, + ::size_t width, + ::size_t height, + ::size_t row_pitch = 0, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateImage2D( + context(), flags,&format, width, height, row_pitch, host_ptr, &error); + + detail::errHandler(error, __CREATE_IMAGE2D_ERR); + if (err != NULL) { + *err = error; + } + } + + Image2D() { } + + Image2D(const Image2D& image2D) : Image(image2D) { } + + Image2D& operator = (const Image2D& rhs) + { + if (this != &rhs) { + Image::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image2DGL + * \brief 2D image interface for GL interop. + */ +class Image2DGL : public Image2D +{ +public: + Image2DGL( + const Context& context, + cl_mem_flags flags, + GLenum target, + GLint miplevel, + GLuint texobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLTexture2D( + context(), + flags, + target, + miplevel, + texobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Image2DGL() : Image2D() { } + + Image2DGL(const Image2DGL& image) : Image2D(image) { } + + Image2DGL& operator = (const Image2DGL& rhs) + { + if (this != &rhs) { + Image2D::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image3D + * \brief Image interface for 3D images. + */ +class Image3D : public Image +{ +public: + Image3D( + const Context& context, + cl_mem_flags flags, + ImageFormat format, + ::size_t width, + ::size_t height, + ::size_t depth, + ::size_t row_pitch = 0, + ::size_t slice_pitch = 0, + void* host_ptr = NULL, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateImage3D( + context(), flags, &format, width, height, depth, row_pitch, + slice_pitch, host_ptr, &error); + + detail::errHandler(error, __CREATE_IMAGE3D_ERR); + if (err != NULL) { + *err = error; + } + } + + Image3D() { } + + Image3D(const Image3D& image3D) : Image(image3D) { } + + Image3D& operator = (const Image3D& rhs) + { + if (this != &rhs) { + Image::operator=(rhs); + } + return *this; + } +}; + +/*! \class Image2DGL + * \brief 2D image interface for GL interop. + */ +class Image3DGL : public Image3D +{ +public: + Image3DGL( + const Context& context, + cl_mem_flags flags, + GLenum target, + GLint miplevel, + GLuint texobj, + cl_int * err = NULL) + { + cl_int error; + object_ = ::clCreateFromGLTexture3D( + context(), + flags, + target, + miplevel, + texobj, + &error); + + detail::errHandler(error, __CREATE_GL_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + } + + Image3DGL() : Image3D() { } + + Image3DGL(const Image3DGL& image) : Image3D(image) { } + + Image3DGL& operator = (const Image3DGL& rhs) + { + if (this != &rhs) { + Image3D::operator=(rhs); + } + return *this; + } +}; + +/*! \class Sampler + * \brief Sampler interface for cl_sampler. + */ +class Sampler : public detail::Wrapper +{ +public: + Sampler() { } + + Sampler( + const Context& context, + cl_bool normalized_coords, + cl_addressing_mode addressing_mode, + cl_filter_mode filter_mode, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateSampler( + context(), + normalized_coords, + addressing_mode, + filter_mode, + &error); + + detail::errHandler(error, __CREATE_SAMPLER_ERR); + if (err != NULL) { + *err = error; + } + } + + Sampler(const Sampler& sampler) : detail::Wrapper(sampler) { } + + Sampler& operator = (const Sampler& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_sampler_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetSamplerInfo, object_, name, param), + __GET_SAMPLER_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_sampler_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Sampler) + +class Program; +class CommandQueue; +class Kernel; + +/*! \class NDRange + * \brief NDRange interface + */ +class NDRange +{ +private: + size_t<3> sizes_; + cl_uint dimensions_; + +public: + NDRange() + : dimensions_(0) + { } + + NDRange(::size_t size0) + : dimensions_(1) + { + sizes_.push_back(size0); + } + + NDRange(::size_t size0, ::size_t size1) + : dimensions_(2) + { + sizes_.push_back(size0); + sizes_.push_back(size1); + } + + NDRange(::size_t size0, ::size_t size1, ::size_t size2) + : dimensions_(3) + { + sizes_.push_back(size0); + sizes_.push_back(size1); + sizes_.push_back(size2); + } + + operator const ::size_t*() const { return (const ::size_t*) sizes_; } + ::size_t dimensions() const { return dimensions_; } +}; + +static const NDRange NullRange; + +/*! + * \struct LocalSpaceArg + * \brief Local address raper for use with Kernel::setArg + */ +struct LocalSpaceArg +{ + ::size_t size_; +}; + +namespace detail { + +template +struct KernelArgumentHandler +{ + static ::size_t size(const T&) { return sizeof(T); } + static T* ptr(T& value) { return &value; } +}; + +template <> +struct KernelArgumentHandler +{ + static ::size_t size(const LocalSpaceArg& value) { return value.size_; } + static void* ptr(LocalSpaceArg&) { return NULL; } +}; + +} +//! \endcond + +inline LocalSpaceArg +__local(::size_t size) +{ + LocalSpaceArg ret = { size }; + return ret; +} + +class KernelFunctor; + +/*! \class Kernel + * \brief Kernel interface that implements cl_kernel + */ +class Kernel : public detail::Wrapper +{ +public: + inline Kernel(const Program& program, const char* name, cl_int* err = NULL); + + Kernel() { } + + Kernel(const Kernel& kernel) : detail::Wrapper(kernel) { } + + Kernel& operator = (const Kernel& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_kernel_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetKernelInfo, object_, name, param), + __GET_KERNEL_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_kernel_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getWorkGroupInfo( + const Device& device, cl_kernel_work_group_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetKernelWorkGroupInfo, object_, device(), name, param), + __GET_KERNEL_WORK_GROUP_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getWorkGroupInfo(const Device& device, cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_kernel_work_group_info, name>::param_type param; + cl_int result = getWorkGroupInfo(device, name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int setArg(cl_uint index, T value) + { + return detail::errHandler( + ::clSetKernelArg( + object_, + index, + detail::KernelArgumentHandler::size(value), + detail::KernelArgumentHandler::ptr(value)), + __SET_KERNEL_ARGS_ERR); + } + + cl_int setArg(cl_uint index, ::size_t size, void* argPtr) + { + return detail::errHandler( + ::clSetKernelArg(object_, index, size, argPtr), + __SET_KERNEL_ARGS_ERR); + } + + KernelFunctor bind( + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local); + + KernelFunctor bind( + const CommandQueue& queue, + const NDRange& global, + const NDRange& local); +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Kernel) + +/*! \class Program + * \brief Program interface that implements cl_program. + */ +class Program : public detail::Wrapper +{ +public: + typedef VECTOR_CLASS > Binaries; + typedef VECTOR_CLASS > Sources; + + Program( + const Context& context, + const Sources& sources, + cl_int* err = NULL) + { + cl_int error; + + const ::size_t n = (::size_t)sources.size(); + ::size_t* lengths = (::size_t*) alloca(n * sizeof(::size_t)); + const char** strings = (const char**) alloca(n * sizeof(const char*)); + + for (::size_t i = 0; i < n; ++i) { + strings[i] = sources[(int)i].first; + lengths[i] = sources[(int)i].second; + } + + object_ = ::clCreateProgramWithSource( + context(), (cl_uint)n, strings, lengths, &error); + + detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); + if (err != NULL) { + *err = error; + } + } + + Program( + const Context& context, + const VECTOR_CLASS& devices, + const Binaries& binaries, + VECTOR_CLASS* binaryStatus = NULL, + cl_int* err = NULL) + { + cl_int error; + const ::size_t n = binaries.size(); + ::size_t* lengths = (::size_t*) alloca(n * sizeof(::size_t)); + const unsigned char** images = (const unsigned char**) alloca(n * sizeof(const void*)); + + for (::size_t i = 0; i < n; ++i) { + images[i] = (const unsigned char*)binaries[(int)i].first; + lengths[i] = binaries[(int)i].second; + } + + object_ = ::clCreateProgramWithBinary( + context(), (cl_uint) devices.size(), + (cl_device_id*)&devices.front(), + lengths, images, binaryStatus != NULL + ? (cl_int*) &binaryStatus->front() + : NULL, &error); + + detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); + if (err != NULL) { + *err = error; + } + } + + Program() { } + + Program(const Program& program) : detail::Wrapper(program) { } + + Program& operator = (const Program& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + cl_int build( + const VECTOR_CLASS& devices, + const char* options = NULL, + void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL, + void* data = NULL) const + { + return detail::errHandler( + ::clBuildProgram( + object_, + (cl_uint) + devices.size(), + (cl_device_id*)&devices.front(), + options, + notifyFptr, + data), + __BUILD_PROGRAM_ERR); + } + + template + cl_int getInfo(cl_program_info name, T* param) const + { + return detail::errHandler( + detail::getInfo(&::clGetProgramInfo, object_, name, param), + __GET_PROGRAM_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_program_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + template + cl_int getBuildInfo( + const Device& device, cl_program_build_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetProgramBuildInfo, object_, device(), name, param), + __GET_PROGRAM_BUILD_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getBuildInfo(const Device& device, cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_program_build_info, name>::param_type param; + cl_int result = getBuildInfo(device, name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int createKernels(VECTOR_CLASS* kernels) + { + cl_uint numKernels; + cl_int err = ::clCreateKernelsInProgram(object_, 0, NULL, &numKernels); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); + } + + Kernel* value = (Kernel*) alloca(numKernels * sizeof(Kernel)); + err = ::clCreateKernelsInProgram( + object_, numKernels, (cl_kernel*) value, NULL); + if (err != CL_SUCCESS) { + return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); + } + + kernels->assign(&value[0], &value[numKernels]); + return CL_SUCCESS; + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::Program) + +inline Kernel::Kernel(const Program& program, const char* name, cl_int* err) +{ + cl_int error; + + object_ = ::clCreateKernel(program(), name, &error); + detail::errHandler(error, __CREATE_KERNEL_ERR); + + if (err != NULL) { + *err = error; + } + +} + +/*! \class CommandQueue + * \brief CommandQueue interface for cl_command_queue. + */ +class CommandQueue : public detail::Wrapper +{ +public: + CommandQueue( + const Context& context, + const Device& device, + cl_command_queue_properties properties = 0, + cl_int* err = NULL) + { + cl_int error; + object_ = ::clCreateCommandQueue( + context(), device(), properties, &error); + + detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); + if (err != NULL) { + *err = error; + } + } + + CommandQueue() { } + + CommandQueue(const CommandQueue& commandQueue) : detail::Wrapper(commandQueue) { } + + CommandQueue& operator = (const CommandQueue& rhs) + { + if (this != &rhs) { + detail::Wrapper::operator=(rhs); + } + return *this; + } + + template + cl_int getInfo(cl_command_queue_info name, T* param) const + { + return detail::errHandler( + detail::getInfo( + &::clGetCommandQueueInfo, object_, name, param), + __GET_COMMAND_QUEUE_INFO_ERR); + } + + template typename + detail::param_traits::param_type + getInfo(cl_int* err = NULL) const + { + typename detail::param_traits< + detail::cl_command_queue_info, name>::param_type param; + cl_int result = getInfo(name, ¶m); + if (err != NULL) { + *err = result; + } + return param; + } + + cl_int enqueueReadBuffer( + const Buffer& buffer, + cl_bool blocking, + ::size_t offset, + ::size_t size, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadBuffer( + object_, buffer(), blocking, offset, size, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_BUFFER_ERR); + } + + cl_int enqueueWriteBuffer( + const Buffer& buffer, + cl_bool blocking, + ::size_t offset, + ::size_t size, + const void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteBuffer( + object_, buffer(), blocking, offset, size, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_BUFFER_ERR); + } + + cl_int enqueueCopyBuffer( + const Buffer& src, + const Buffer& dst, + ::size_t src_offset, + ::size_t dst_offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBuffer( + object_, src(), dst(), src_offset, dst_offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQEUE_COPY_BUFFER_ERR); + } + +#if defined(CL_VERSION_1_1) + cl_int enqueueReadBufferRect( + const Buffer& buffer, + cl_bool blocking, + const size_t<3>& buffer_offset, + const size_t<3>& host_offset, + const size_t<3>& region, + ::size_t buffer_row_pitch, + ::size_t buffer_slice_pitch, + ::size_t host_row_pitch, + ::size_t host_slice_pitch, + void *ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadBufferRect( + object_, + buffer(), + blocking, + (const ::size_t *)buffer_offset, + (const ::size_t *)host_offset, + (const ::size_t *)region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_BUFFER_RECT_ERR); + } + + + cl_int enqueueWriteBufferRect( + const Buffer& buffer, + cl_bool blocking, + const size_t<3>& buffer_offset, + const size_t<3>& host_offset, + const size_t<3>& region, + ::size_t buffer_row_pitch, + ::size_t buffer_slice_pitch, + ::size_t host_row_pitch, + ::size_t host_slice_pitch, + void *ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteBufferRect( + object_, + buffer(), + blocking, + (const ::size_t *)buffer_offset, + (const ::size_t *)host_offset, + (const ::size_t *)region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_BUFFER_RECT_ERR); + } + + cl_int enqueueCopyBufferRect( + const Buffer& src, + const Buffer& dst, + const size_t<3>& src_origin, + const size_t<3>& dst_origin, + const size_t<3>& region, + ::size_t src_row_pitch, + ::size_t src_slice_pitch, + ::size_t dst_row_pitch, + ::size_t dst_slice_pitch, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBufferRect( + object_, + src(), + dst(), + (const ::size_t *)src_origin, + (const ::size_t *)dst_origin, + (const ::size_t *)region, + src_row_pitch, + src_slice_pitch, + dst_row_pitch, + dst_slice_pitch, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQEUE_COPY_BUFFER_RECT_ERR); + } +#endif + + cl_int enqueueReadImage( + const Image& image, + cl_bool blocking, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t row_pitch, + ::size_t slice_pitch, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReadImage( + object_, image(), blocking, (const ::size_t *) origin, + (const ::size_t *) region, row_pitch, slice_pitch, ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_READ_IMAGE_ERR); + } + + cl_int enqueueWriteImage( + const Image& image, + cl_bool blocking, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t row_pitch, + ::size_t slice_pitch, + void* ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueWriteImage( + object_, image(), blocking, (const ::size_t *) origin, + (const ::size_t *) region, row_pitch, slice_pitch, ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_WRITE_IMAGE_ERR); + } + + cl_int enqueueCopyImage( + const Image& src, + const Image& dst, + const size_t<3>& src_origin, + const size_t<3>& dst_origin, + const size_t<3>& region, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyImage( + object_, src(), dst(), (const ::size_t *) src_origin, + (const ::size_t *)dst_origin, (const ::size_t *) region, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_IMAGE_ERR); + } + + cl_int enqueueCopyImageToBuffer( + const Image& src, + const Buffer& dst, + const size_t<3>& src_origin, + const size_t<3>& region, + ::size_t dst_offset, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyImageToBuffer( + object_, src(), dst(), (const ::size_t *) src_origin, + (const ::size_t *) region, dst_offset, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR); + } + + cl_int enqueueCopyBufferToImage( + const Buffer& src, + const Image& dst, + ::size_t src_offset, + const size_t<3>& dst_origin, + const size_t<3>& region, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueCopyBufferToImage( + object_, src(), dst(), src_offset, + (const ::size_t *) dst_origin, (const ::size_t *) region, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR); + } + + void* enqueueMapBuffer( + const Buffer& buffer, + cl_bool blocking, + cl_map_flags flags, + ::size_t offset, + ::size_t size, + const VECTOR_CLASS* events = NULL, + Event* event = NULL, + cl_int* err = NULL) const + { + cl_int error; + void * result = ::clEnqueueMapBuffer( + object_, buffer(), blocking, flags, offset, size, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event, + &error); + + detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); + if (err != NULL) { + *err = error; + } + return result; + } + + void* enqueueMapImage( + const Image& buffer, + cl_bool blocking, + cl_map_flags flags, + const size_t<3>& origin, + const size_t<3>& region, + ::size_t * row_pitch, + ::size_t * slice_pitch, + const VECTOR_CLASS* events = NULL, + Event* event = NULL, + cl_int* err = NULL) const + { + cl_int error; + void * result = ::clEnqueueMapImage( + object_, buffer(), blocking, flags, + (const ::size_t *) origin, (const ::size_t *) region, + row_pitch, slice_pitch, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event, + &error); + + detail::errHandler(error, __ENQUEUE_MAP_IMAGE_ERR); + if (err != NULL) { + *err = error; + } + return result; + } + + cl_int enqueueUnmapMemObject( + const Memory& memory, + void* mapped_ptr, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueUnmapMemObject( + object_, memory(), mapped_ptr, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_UNMAP_MEM_OBJECT_ERR); + } + + cl_int enqueueNDRangeKernel( + const Kernel& kernel, + const NDRange& offset, + const NDRange& global, + const NDRange& local, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueNDRangeKernel( + object_, kernel(), (cl_uint) global.dimensions(), + offset.dimensions() != 0 ? (const ::size_t*) offset : NULL, + (const ::size_t*) global, + local.dimensions() != 0 ? (const ::size_t*) local : NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_NDRANGE_KERNEL_ERR); + } + + cl_int enqueueTask( + const Kernel& kernel, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueTask( + object_, kernel(), + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_TASK_ERR); + } + + cl_int enqueueNativeKernel( + void (*userFptr)(void *), + std::pair args, + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* mem_locs = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + cl_mem * mems = (mem_objects != NULL && mem_objects->size() > 0) + ? (cl_mem*) alloca(mem_objects->size() * sizeof(cl_mem)) + : NULL; + + if (mems != NULL) { + for (unsigned int i = 0; i < mem_objects->size(); i++) { + mems[i] = ((*mem_objects)[i])(); + } + } + + return detail::errHandler( + ::clEnqueueNativeKernel( + object_, userFptr, args.first, args.second, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + mems, + (mem_locs != NULL) ? (const void **) &mem_locs->front() : NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_NATIVE_KERNEL); + } + + cl_int enqueueMarker(Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueMarker(object_, (cl_event*) event), + __ENQUEUE_MARKER_ERR); + } + + cl_int enqueueWaitForEvents(const VECTOR_CLASS& events) const + { + return detail::errHandler( + ::clEnqueueWaitForEvents( + object_, + (cl_uint) events.size(), + (const cl_event*) &events.front()), + __ENQUEUE_WAIT_FOR_EVENTS_ERR); + } + + cl_int enqueueAcquireGLObjects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueAcquireGLObjects( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_ACQUIRE_GL_ERR); + } + + cl_int enqueueReleaseGLObjects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + return detail::errHandler( + ::clEnqueueReleaseGLObjects( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL && events->size() > 0) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_RELEASE_GL_ERR); + } + +#if defined (USE_DX_INTEROP) +typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueAcquireD3D10ObjectsKHR)( + cl_command_queue command_queue, cl_uint num_objects, + const cl_mem* mem_objects, cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, cl_event* event); +typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueReleaseD3D10ObjectsKHR)( + cl_command_queue command_queue, cl_uint num_objects, + const cl_mem* mem_objects, cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, cl_event* event); + + cl_int enqueueAcquireD3D10Objects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + static PFN_clEnqueueAcquireD3D10ObjectsKHR pfn_clEnqueueAcquireD3D10ObjectsKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clEnqueueAcquireD3D10ObjectsKHR); + + return detail::errHandler( + pfn_clEnqueueAcquireD3D10ObjectsKHR( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_ACQUIRE_GL_ERR); + } + + cl_int enqueueReleaseD3D10Objects( + const VECTOR_CLASS* mem_objects = NULL, + const VECTOR_CLASS* events = NULL, + Event* event = NULL) const + { + static PFN_clEnqueueReleaseD3D10ObjectsKHR pfn_clEnqueueReleaseD3D10ObjectsKHR = NULL; + __INIT_CL_EXT_FCN_PTR(clEnqueueReleaseD3D10ObjectsKHR); + + return detail::errHandler( + pfn_clEnqueueReleaseD3D10ObjectsKHR( + object_, + (mem_objects != NULL) ? (cl_uint) mem_objects->size() : 0, + (mem_objects != NULL) ? (const cl_mem *) &mem_objects->front(): NULL, + (events != NULL) ? (cl_uint) events->size() : 0, + (events != NULL) ? (cl_event*) &events->front() : NULL, + (cl_event*) event), + __ENQUEUE_RELEASE_GL_ERR); + } +#endif + + cl_int enqueueBarrier() const + { + return detail::errHandler( + ::clEnqueueBarrier(object_), + __ENQUEUE_BARRIER_ERR); + } + + cl_int flush() const + { + return detail::errHandler(::clFlush(object_), __FLUSH_ERR); + } + + cl_int finish() const + { + return detail::errHandler(::clFinish(object_), __FINISH_ERR); + } +}; + +__GET_INFO_HELPER_WITH_RETAIN(cl::CommandQueue) + +/*! \class KernelFunctor + * \brief Kernel functor interface + * + * \note Currently only functors of zero to ten arguments are supported. It + * is straightforward to add more and a more general solution, similar to + * Boost.Lambda could be followed if required in the future. + */ +class KernelFunctor +{ +private: + Kernel kernel_; + CommandQueue queue_; + NDRange offset_; + NDRange global_; + NDRange local_; + + cl_int err_; +public: + KernelFunctor() { } + + KernelFunctor( + const Kernel& kernel, + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local) : + kernel_(kernel), + queue_(queue), + offset_(offset), + global_(global), + local_(local), + err_(CL_SUCCESS) + {} + + KernelFunctor& operator=(const KernelFunctor& rhs); + + KernelFunctor(const KernelFunctor& rhs); + + cl_int getError() { return err_; } + + inline Event operator()(const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const VECTOR_CLASS* events = NULL); + + template + inline Event operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const A15& a15, + const VECTOR_CLASS* events = NULL); +}; + +inline KernelFunctor Kernel::bind( + const CommandQueue& queue, + const NDRange& offset, + const NDRange& global, + const NDRange& local) +{ + return KernelFunctor(*this,queue,offset,global,local); +} + +inline KernelFunctor Kernel::bind( + const CommandQueue& queue, + const NDRange& global, + const NDRange& local) +{ + return KernelFunctor(*this,queue,NullRange,global,local); +} + +inline KernelFunctor& KernelFunctor::operator=(const KernelFunctor& rhs) +{ + if (this == &rhs) { + return *this; + } + + kernel_ = rhs.kernel_; + queue_ = rhs.queue_; + offset_ = rhs.offset_; + global_ = rhs.global_; + local_ = rhs.local_; + + return *this; +} + +inline KernelFunctor::KernelFunctor(const KernelFunctor& rhs) : + kernel_(rhs.kernel_), + queue_(rhs.queue_), + offset_(rhs.offset_), + global_(rhs.global_), + local_(rhs.local_) +{ +} + +Event KernelFunctor::operator()(const VECTOR_CLASS* events) +{ + (void)events; + Event event; + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + kernel_.setArg(13,a14); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +template +Event KernelFunctor::operator()( + const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6, + const A7& a7, + const A8& a8, + const A9& a9, + const A10& a10, + const A11& a11, + const A12& a12, + const A13& a13, + const A14& a14, + const A15& a15, + const VECTOR_CLASS* events) +{ + Event event; + + kernel_.setArg(0,a1); + kernel_.setArg(1,a2); + kernel_.setArg(2,a3); + kernel_.setArg(3,a4); + kernel_.setArg(4,a5); + kernel_.setArg(5,a6); + kernel_.setArg(6,a7); + kernel_.setArg(7,a8); + kernel_.setArg(8,a9); + kernel_.setArg(9,a10); + kernel_.setArg(10,a11); + kernel_.setArg(11,a12); + kernel_.setArg(12,a13); + kernel_.setArg(13,a14); + kernel_.setArg(14,a15); + + err_ = queue_.enqueueNDRangeKernel( + kernel_, + offset_, + global_, + local_, + NULL, // bgaster_fixme - do we want to allow wait event lists? + &event); + + return event; +} + +#undef __ERR_STR +#if !defined(__CL_USER_OVERRIDE_ERROR_STRINGS) +#undef __GET_DEVICE_INFO_ERR +#undef __GET_PLATFORM_INFO_ERR +#undef __GET_DEVICE_IDS_ERR +#undef __GET_CONTEXT_INFO_ERR +#undef __GET_EVENT_INFO_ERR +#undef __GET_EVENT_PROFILE_INFO_ERR +#undef __GET_MEM_OBJECT_INFO_ERR +#undef __GET_IMAGE_INFO_ERR +#undef __GET_SAMPLER_INFO_ERR +#undef __GET_KERNEL_INFO_ERR +#undef __GET_KERNEL_WORK_GROUP_INFO_ERR +#undef __GET_PROGRAM_INFO_ERR +#undef __GET_PROGRAM_BUILD_INFO_ERR +#undef __GET_COMMAND_QUEUE_INFO_ERR + +#undef __CREATE_CONTEXT_FROM_TYPE_ERR +#undef __GET_SUPPORTED_IMAGE_FORMATS_ERR + +#undef __CREATE_BUFFER_ERR +#undef __CREATE_SUBBUFFER_ERR +#undef __CREATE_IMAGE2D_ERR +#undef __CREATE_IMAGE3D_ERR +#undef __CREATE_SAMPLER_ERR +#undef __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR + +#undef __CREATE_USER_EVENT_ERR +#undef __SET_USER_EVENT_STATUS_ERR +#undef __SET_EVENT_CALLBACK_ERR + +#undef __WAIT_FOR_EVENTS_ERR + +#undef __CREATE_KERNEL_ERR +#undef __SET_KERNEL_ARGS_ERR +#undef __CREATE_PROGRAM_WITH_SOURCE_ERR +#undef __CREATE_PROGRAM_WITH_BINARY_ERR +#undef __BUILD_PROGRAM_ERR +#undef __CREATE_KERNELS_IN_PROGRAM_ERR + +#undef __CREATE_COMMAND_QUEUE_ERR +#undef __SET_COMMAND_QUEUE_PROPERTY_ERR +#undef __ENQUEUE_READ_BUFFER_ERR +#undef __ENQUEUE_WRITE_BUFFER_ERR +#undef __ENQUEUE_READ_BUFFER_RECT_ERR +#undef __ENQUEUE_WRITE_BUFFER_RECT_ERR +#undef __ENQEUE_COPY_BUFFER_ERR +#undef __ENQEUE_COPY_BUFFER_RECT_ERR +#undef __ENQUEUE_READ_IMAGE_ERR +#undef __ENQUEUE_WRITE_IMAGE_ERR +#undef __ENQUEUE_COPY_IMAGE_ERR +#undef __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR +#undef __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR +#undef __ENQUEUE_MAP_BUFFER_ERR +#undef __ENQUEUE_MAP_IMAGE_ERR +#undef __ENQUEUE_UNMAP_MEM_OBJECT_ERR +#undef __ENQUEUE_NDRANGE_KERNEL_ERR +#undef __ENQUEUE_TASK_ERR +#undef __ENQUEUE_NATIVE_KERNEL + +#undef __UNLOAD_COMPILER_ERR +#endif //__CL_USER_OVERRIDE_ERROR_STRINGS + +#undef __GET_INFO_HELPER_WITH_RETAIN + +// Extensions +#undef __INIT_CL_EXT_FCN_PTR +#undef __CREATE_SUB_DEVICES + +#if defined(USE_CL_DEVICE_FISSION) +#undef __PARAM_NAME_DEVICE_FISSION +#endif // USE_CL_DEVICE_FISSION + +} // namespace cl + +#endif // CL_HPP_ diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp new file mode 100644 index 000000000..96f1fa582 --- /dev/null +++ b/libethash-cl/ethash_cl_miner.cpp @@ -0,0 +1,332 @@ +/* + This file is part of c-ethash. + + c-ethash 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. + + c-ethash 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 ethash_cl_miner.cpp +* @author Tim Hughes +* @date 2015 +*/ + + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include +#include "ethash_cl_miner.h" +#include "ethash_cl_miner_kernel.h" + +#define ETHASH_BYTES 32 + +// workaround lame platforms +#if !CL_VERSION_1_2 +#define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE +#define CL_MEM_HOST_READ_ONLY 0 +#endif + +#undef min +#undef max + +static void add_definition(std::string& source, char const* id, unsigned value) +{ + char buf[256]; + sprintf(buf, "#define %s %uu\n", id, value); + source.insert(source.begin(), buf, buf + strlen(buf)); +} + +ethash_cl_miner::ethash_cl_miner() +: m_opencl_1_1() +{ +} + +void ethash_cl_miner::finish() +{ + if (m_queue()) + { + m_queue.finish(); + } +} + +bool ethash_cl_miner::init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size) +{ + // store params + m_params = params; + + // get all platforms + std::vector platforms; + cl::Platform::get(&platforms); + if (platforms.empty()) + { + debugf("No OpenCL platforms found.\n"); + return false; + } + + // use default platform + fprintf(stderr, "Using platform: %s\n", platforms[0].getInfo().c_str()); + + // get GPU device of the default platform + std::vector devices; + platforms[0].getDevices(CL_DEVICE_TYPE_ALL, &devices); + if (devices.empty()) + { + debugf("No OpenCL devices found.\n"); + return false; + } + + // use default device + unsigned device_num = 0; + cl::Device& device = devices[device_num]; + std::string device_version = device.getInfo(); + fprintf(stderr, "Using device: %s (%s)\n", device.getInfo().c_str(),device_version.c_str()); + + if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0) + { + debugf("OpenCL 1.0 is not supported.\n"); + return false; + } + if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0) + { + m_opencl_1_1 = true; + } + + // create context + m_context = cl::Context(std::vector(&device, &device + 1)); + m_queue = cl::CommandQueue(m_context, device); + + // use requested workgroup size, but we require multiple of 8 + m_workgroup_size = ((workgroup_size + 7) / 8) * 8; + + // patch source code + std::string code(ETHASH_CL_MINER_KERNEL, ETHASH_CL_MINER_KERNEL + ETHASH_CL_MINER_KERNEL_SIZE); + add_definition(code, "GROUP_SIZE", m_workgroup_size); + add_definition(code, "DAG_SIZE", (unsigned)(params.full_size / ETHASH_MIX_BYTES)); + add_definition(code, "ACCESSES", ETHASH_ACCESSES); + add_definition(code, "MAX_OUTPUTS", c_max_search_results); + //debugf("%s", code.c_str()); + + // create miner OpenCL program + cl::Program::Sources sources; + sources.push_back({code.c_str(), code.size()}); + + cl::Program program(m_context, sources); + try + { + program.build({device}); + } + catch (cl::Error err) + { + debugf("%s\n", program.getBuildInfo(device).c_str()); + return false; + } + m_hash_kernel = cl::Kernel(program, "ethash_hash"); + m_search_kernel = cl::Kernel(program, "ethash_search"); + + // create buffer for dag + m_dag = cl::Buffer(m_context, CL_MEM_READ_ONLY, params.full_size); + + // create buffer for header + m_header = cl::Buffer(m_context, CL_MEM_READ_ONLY, 32); + + // compute dag on CPU + { + // if this throws then it's because we probably need to subdivide the dag uploads for compatibility + void* dag_ptr = m_queue.enqueueMapBuffer(m_dag, true, m_opencl_1_1 ? CL_MAP_WRITE : CL_MAP_WRITE_INVALIDATE_REGION, 0, params.full_size); + // memcpying 1GB: horrible... really. horrible. but necessary since we can't mmap *and* gpumap. + _fillDAG(dag_ptr); + m_queue.enqueueUnmapMemObject(m_dag, dag_ptr); + } + + // create mining buffers + for (unsigned i = 0; i != c_num_buffers; ++i) + { + m_hash_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY | (!m_opencl_1_1 ? CL_MEM_HOST_READ_ONLY : 0), 32*c_hash_batch_size); + m_search_buf[i] = cl::Buffer(m_context, CL_MEM_WRITE_ONLY, (c_max_search_results + 1) * sizeof(uint32_t)); + } + return true; +} + +void ethash_cl_miner::hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count) +{ + struct pending_batch + { + unsigned base; + unsigned count; + unsigned buf; + }; + std::queue pending; + + // update header constant buffer + m_queue.enqueueWriteBuffer(m_header, true, 0, 32, header); + + /* + __kernel void ethash_combined_hash( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) + */ + m_hash_kernel.setArg(1, m_header); + m_hash_kernel.setArg(2, m_dag); + m_hash_kernel.setArg(3, nonce); + m_hash_kernel.setArg(4, ~0u); // have to pass this to stop the compile unrolling the loop + + unsigned buf = 0; + for (unsigned i = 0; i < count || !pending.empty(); ) + { + // how many this batch + if (i < count) + { + unsigned const this_count = std::min(count - i, c_hash_batch_size); + unsigned const batch_count = std::max(this_count, m_workgroup_size); + + // supply output hash buffer to kernel + m_hash_kernel.setArg(0, m_hash_buf[buf]); + + // execute it! + m_queue.enqueueNDRangeKernel( + m_hash_kernel, + cl::NullRange, + cl::NDRange(batch_count), + cl::NDRange(m_workgroup_size) + ); + m_queue.flush(); + + pending.push({i, this_count, buf}); + i += this_count; + buf = (buf + 1) % c_num_buffers; + } + + // read results + if (i == count || pending.size() == c_num_buffers) + { + pending_batch const& batch = pending.front(); + + // could use pinned host pointer instead, but this path isn't that important. + uint8_t* hashes = (uint8_t*)m_queue.enqueueMapBuffer(m_hash_buf[batch.buf], true, CL_MAP_READ, 0, batch.count * ETHASH_BYTES); + memcpy(ret + batch.base*ETHASH_BYTES, hashes, batch.count*ETHASH_BYTES); + m_queue.enqueueUnmapMemObject(m_hash_buf[batch.buf], hashes); + + pending.pop(); + } + } +} + + +void ethash_cl_miner::search(uint8_t const* header, uint64_t target, search_hook& hook) +{ + struct pending_batch + { + uint64_t start_nonce; + unsigned buf; + }; + std::queue pending; + + static uint32_t const c_zero = 0; + + // update header constant buffer + m_queue.enqueueWriteBuffer(m_header, false, 0, 32, header); + for (unsigned i = 0; i != c_num_buffers; ++i) + { + m_queue.enqueueWriteBuffer(m_search_buf[i], false, 0, 4, &c_zero); + } + +#if CL_VERSION_1_2 && 0 + cl::Event pre_return_event; + if (!m_opencl_1_1) + { + m_queue.enqueueBarrierWithWaitList(NULL, &pre_return_event); + } + else +#endif + { + m_queue.finish(); + } + + /* + __kernel void ethash_combined_search( + __global hash32_t* g_hashes, // 0 + __constant hash32_t const* g_header, // 1 + __global hash128_t const* g_dag, // 2 + ulong start_nonce, // 3 + ulong target, // 4 + uint isolate // 5 + ) + */ + m_search_kernel.setArg(1, m_header); + m_search_kernel.setArg(2, m_dag); + + // pass these to stop the compiler unrolling the loops + m_search_kernel.setArg(4, target); + m_search_kernel.setArg(5, ~0u); + + + unsigned buf = 0; + for (uint64_t start_nonce = 0; ; start_nonce += c_search_batch_size) + { + // supply output buffer to kernel + m_search_kernel.setArg(0, m_search_buf[buf]); + m_search_kernel.setArg(3, start_nonce); + + // execute it! + m_queue.enqueueNDRangeKernel(m_search_kernel, cl::NullRange, c_search_batch_size, m_workgroup_size); + + pending.push({start_nonce, buf}); + buf = (buf + 1) % c_num_buffers; + + // read results + if (pending.size() == c_num_buffers) + { + pending_batch const& batch = pending.front(); + + // could use pinned host pointer instead + uint32_t* results = (uint32_t*)m_queue.enqueueMapBuffer(m_search_buf[batch.buf], true, CL_MAP_READ, 0, (1+c_max_search_results) * sizeof(uint32_t)); + unsigned num_found = std::min(results[0], c_max_search_results); + + uint64_t nonces[c_max_search_results]; + for (unsigned i = 0; i != num_found; ++i) + { + nonces[i] = batch.start_nonce + results[i+1]; + } + + m_queue.enqueueUnmapMemObject(m_search_buf[batch.buf], results); + + bool exit = num_found && hook.found(nonces, num_found); + exit |= hook.searched(batch.start_nonce, c_search_batch_size); // always report searched before exit + if (exit) + break; + + // reset search buffer if we're still going + if (num_found) + m_queue.enqueueWriteBuffer(m_search_buf[batch.buf], true, 0, 4, &c_zero); + + pending.pop(); + } + } + + // not safe to return until this is ready +#if CL_VERSION_1_2 && 0 + if (!m_opencl_1_1) + { + pre_return_event.wait(); + } +#endif +} + diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h new file mode 100644 index 000000000..e478c739f --- /dev/null +++ b/libethash-cl/ethash_cl_miner.h @@ -0,0 +1,43 @@ +#pragma once + +#define __CL_ENABLE_EXCEPTIONS +#define CL_USE_DEPRECATED_OPENCL_2_0_APIS +#include "cl.hpp" +#include +#include +#include + +class ethash_cl_miner +{ +public: + struct search_hook + { + // reports progress, return true to abort + virtual bool found(uint64_t const* nonces, uint32_t count) = 0; + virtual bool searched(uint64_t start_nonce, uint32_t count) = 0; + }; + +public: + ethash_cl_miner(); + + bool init(ethash_params const& params, std::function _fillDAG, unsigned workgroup_size = 64); + + void finish(); + void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count); + void search(uint8_t const* header, uint64_t target, search_hook& hook); + +private: + enum { c_max_search_results = 63, c_num_buffers = 2, c_hash_batch_size = 1024, c_search_batch_size = 1024*256 }; + + ethash_params m_params; + cl::Context m_context; + cl::CommandQueue m_queue; + cl::Kernel m_hash_kernel; + cl::Kernel m_search_kernel; + cl::Buffer m_dag; + cl::Buffer m_header; + cl::Buffer m_hash_buf[c_num_buffers]; + cl::Buffer m_search_buf[c_num_buffers]; + unsigned m_workgroup_size; + bool m_opencl_1_1; +}; diff --git a/libethash-cl/ethash_cl_miner_kernel.cl b/libethash-cl/ethash_cl_miner_kernel.cl new file mode 100644 index 000000000..3c8b9dc92 --- /dev/null +++ b/libethash-cl/ethash_cl_miner_kernel.cl @@ -0,0 +1,460 @@ +// author Tim Hughes +// Tested on Radeon HD 7850 +// Hashrate: 15940347 hashes/s +// Bandwidth: 124533 MB/s +// search kernel should fit in <= 84 VGPRS (3 wavefronts) + +#define THREADS_PER_HASH (128 / 16) +#define HASHES_PER_LOOP (GROUP_SIZE / THREADS_PER_HASH) + +#define FNV_PRIME 0x01000193 + +__constant uint2 const Keccak_f1600_RC[24] = { + (uint2)(0x00000001, 0x00000000), + (uint2)(0x00008082, 0x00000000), + (uint2)(0x0000808a, 0x80000000), + (uint2)(0x80008000, 0x80000000), + (uint2)(0x0000808b, 0x00000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008009, 0x80000000), + (uint2)(0x0000008a, 0x00000000), + (uint2)(0x00000088, 0x00000000), + (uint2)(0x80008009, 0x00000000), + (uint2)(0x8000000a, 0x00000000), + (uint2)(0x8000808b, 0x00000000), + (uint2)(0x0000008b, 0x80000000), + (uint2)(0x00008089, 0x80000000), + (uint2)(0x00008003, 0x80000000), + (uint2)(0x00008002, 0x80000000), + (uint2)(0x00000080, 0x80000000), + (uint2)(0x0000800a, 0x00000000), + (uint2)(0x8000000a, 0x80000000), + (uint2)(0x80008081, 0x80000000), + (uint2)(0x00008080, 0x80000000), + (uint2)(0x80000001, 0x00000000), + (uint2)(0x80008008, 0x80000000), +}; + +void keccak_f1600_round(uint2* a, uint r, uint out_size) +{ + #if !__ENDIAN_LITTLE__ + for (uint i = 0; i != 25; ++i) + a[i] = a[i].yx; + #endif + + uint2 b[25]; + uint2 t; + + // Theta + b[0] = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]; + b[1] = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]; + b[2] = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]; + b[3] = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]; + b[4] = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]; + t = b[4] ^ (uint2)(b[1].x << 1 | b[1].y >> 31, b[1].y << 1 | b[1].x >> 31); + a[0] ^= t; + a[5] ^= t; + a[10] ^= t; + a[15] ^= t; + a[20] ^= t; + t = b[0] ^ (uint2)(b[2].x << 1 | b[2].y >> 31, b[2].y << 1 | b[2].x >> 31); + a[1] ^= t; + a[6] ^= t; + a[11] ^= t; + a[16] ^= t; + a[21] ^= t; + t = b[1] ^ (uint2)(b[3].x << 1 | b[3].y >> 31, b[3].y << 1 | b[3].x >> 31); + a[2] ^= t; + a[7] ^= t; + a[12] ^= t; + a[17] ^= t; + a[22] ^= t; + t = b[2] ^ (uint2)(b[4].x << 1 | b[4].y >> 31, b[4].y << 1 | b[4].x >> 31); + a[3] ^= t; + a[8] ^= t; + a[13] ^= t; + a[18] ^= t; + a[23] ^= t; + t = b[3] ^ (uint2)(b[0].x << 1 | b[0].y >> 31, b[0].y << 1 | b[0].x >> 31); + a[4] ^= t; + a[9] ^= t; + a[14] ^= t; + a[19] ^= t; + a[24] ^= t; + + // Rho Pi + b[0] = a[0]; + b[10] = (uint2)(a[1].x << 1 | a[1].y >> 31, a[1].y << 1 | a[1].x >> 31); + b[7] = (uint2)(a[10].x << 3 | a[10].y >> 29, a[10].y << 3 | a[10].x >> 29); + b[11] = (uint2)(a[7].x << 6 | a[7].y >> 26, a[7].y << 6 | a[7].x >> 26); + b[17] = (uint2)(a[11].x << 10 | a[11].y >> 22, a[11].y << 10 | a[11].x >> 22); + b[18] = (uint2)(a[17].x << 15 | a[17].y >> 17, a[17].y << 15 | a[17].x >> 17); + b[3] = (uint2)(a[18].x << 21 | a[18].y >> 11, a[18].y << 21 | a[18].x >> 11); + b[5] = (uint2)(a[3].x << 28 | a[3].y >> 4, a[3].y << 28 | a[3].x >> 4); + b[16] = (uint2)(a[5].y << 4 | a[5].x >> 28, a[5].x << 4 | a[5].y >> 28); + b[8] = (uint2)(a[16].y << 13 | a[16].x >> 19, a[16].x << 13 | a[16].y >> 19); + b[21] = (uint2)(a[8].y << 23 | a[8].x >> 9, a[8].x << 23 | a[8].y >> 9); + b[24] = (uint2)(a[21].x << 2 | a[21].y >> 30, a[21].y << 2 | a[21].x >> 30); + b[4] = (uint2)(a[24].x << 14 | a[24].y >> 18, a[24].y << 14 | a[24].x >> 18); + b[15] = (uint2)(a[4].x << 27 | a[4].y >> 5, a[4].y << 27 | a[4].x >> 5); + b[23] = (uint2)(a[15].y << 9 | a[15].x >> 23, a[15].x << 9 | a[15].y >> 23); + b[19] = (uint2)(a[23].y << 24 | a[23].x >> 8, a[23].x << 24 | a[23].y >> 8); + b[13] = (uint2)(a[19].x << 8 | a[19].y >> 24, a[19].y << 8 | a[19].x >> 24); + b[12] = (uint2)(a[13].x << 25 | a[13].y >> 7, a[13].y << 25 | a[13].x >> 7); + b[2] = (uint2)(a[12].y << 11 | a[12].x >> 21, a[12].x << 11 | a[12].y >> 21); + b[20] = (uint2)(a[2].y << 30 | a[2].x >> 2, a[2].x << 30 | a[2].y >> 2); + b[14] = (uint2)(a[20].x << 18 | a[20].y >> 14, a[20].y << 18 | a[20].x >> 14); + b[22] = (uint2)(a[14].y << 7 | a[14].x >> 25, a[14].x << 7 | a[14].y >> 25); + b[9] = (uint2)(a[22].y << 29 | a[22].x >> 3, a[22].x << 29 | a[22].y >> 3); + b[6] = (uint2)(a[9].x << 20 | a[9].y >> 12, a[9].y << 20 | a[9].x >> 12); + b[1] = (uint2)(a[6].y << 12 | a[6].x >> 20, a[6].x << 12 | a[6].y >> 20); + + // Chi + a[0] = bitselect(b[0] ^ b[2], b[0], b[1]); + a[1] = bitselect(b[1] ^ b[3], b[1], b[2]); + a[2] = bitselect(b[2] ^ b[4], b[2], b[3]); + a[3] = bitselect(b[3] ^ b[0], b[3], b[4]); + if (out_size >= 4) + { + a[4] = bitselect(b[4] ^ b[1], b[4], b[0]); + a[5] = bitselect(b[5] ^ b[7], b[5], b[6]); + a[6] = bitselect(b[6] ^ b[8], b[6], b[7]); + a[7] = bitselect(b[7] ^ b[9], b[7], b[8]); + a[8] = bitselect(b[8] ^ b[5], b[8], b[9]); + if (out_size >= 8) + { + a[9] = bitselect(b[9] ^ b[6], b[9], b[5]); + a[10] = bitselect(b[10] ^ b[12], b[10], b[11]); + a[11] = bitselect(b[11] ^ b[13], b[11], b[12]); + a[12] = bitselect(b[12] ^ b[14], b[12], b[13]); + a[13] = bitselect(b[13] ^ b[10], b[13], b[14]); + a[14] = bitselect(b[14] ^ b[11], b[14], b[10]); + a[15] = bitselect(b[15] ^ b[17], b[15], b[16]); + a[16] = bitselect(b[16] ^ b[18], b[16], b[17]); + a[17] = bitselect(b[17] ^ b[19], b[17], b[18]); + a[18] = bitselect(b[18] ^ b[15], b[18], b[19]); + a[19] = bitselect(b[19] ^ b[16], b[19], b[15]); + a[20] = bitselect(b[20] ^ b[22], b[20], b[21]); + a[21] = bitselect(b[21] ^ b[23], b[21], b[22]); + a[22] = bitselect(b[22] ^ b[24], b[22], b[23]); + a[23] = bitselect(b[23] ^ b[20], b[23], b[24]); + a[24] = bitselect(b[24] ^ b[21], b[24], b[20]); + } + } + + // Iota + a[0] ^= Keccak_f1600_RC[r]; + + #if !__ENDIAN_LITTLE__ + for (uint i = 0; i != 25; ++i) + a[i] = a[i].yx; + #endif +} + +void keccak_f1600_no_absorb(ulong* a, uint in_size, uint out_size, uint isolate) +{ + for (uint i = in_size; i != 25; ++i) + { + a[i] = 0; + } +#if __ENDIAN_LITTLE__ + a[in_size] ^= 0x0000000000000001; + a[24-out_size*2] ^= 0x8000000000000000; +#else + a[in_size] ^= 0x0100000000000000; + a[24-out_size*2] ^= 0x0000000000000080; +#endif + + // Originally I unrolled the first and last rounds to interface + // better with surrounding code, however I haven't done this + // without causing the AMD compiler to blow up the VGPR usage. + uint r = 0; + do + { + // This dynamic branch stops the AMD compiler unrolling the loop + // and additionally saves about 33% of the VGPRs, enough to gain another + // wavefront. Ideally we'd get 4 in flight, but 3 is the best I can + // massage out of the compiler. It doesn't really seem to matter how + // much we try and help the compiler save VGPRs because it seems to throw + // that information away, hence the implementation of keccak here + // doesn't bother. + if (isolate) + { + keccak_f1600_round((uint2*)a, r++, 25); + } + } + while (r < 23); + + // final round optimised for digest size + keccak_f1600_round((uint2*)a, r++, out_size); +} + +#define copy(dst, src, count) for (uint i = 0; i != count; ++i) { (dst)[i] = (src)[i]; } + +#define countof(x) (sizeof(x) / sizeof(x[0])) + +uint fnv(uint x, uint y) +{ + return x * FNV_PRIME ^ y; +} + +uint4 fnv4(uint4 x, uint4 y) +{ + return x * FNV_PRIME ^ y; +} + +uint fnv_reduce(uint4 v) +{ + return fnv(fnv(fnv(v.x, v.y), v.z), v.w); +} + +typedef union +{ + ulong ulongs[32 / sizeof(ulong)]; + uint uints[32 / sizeof(uint)]; +} hash32_t; + +typedef union +{ + ulong ulongs[64 / sizeof(ulong)]; + uint4 uint4s[64 / sizeof(uint4)]; +} hash64_t; + +typedef union +{ + uint uints[128 / sizeof(uint)]; + uint4 uint4s[128 / sizeof(uint4)]; +} hash128_t; + +hash64_t init_hash(__constant hash32_t const* header, ulong nonce, uint isolate) +{ + hash64_t init; + uint const init_size = countof(init.ulongs); + uint const hash_size = countof(header->ulongs); + + // sha3_512(header .. nonce) + ulong state[25]; + copy(state, header->ulongs, hash_size); + state[hash_size] = nonce; + keccak_f1600_no_absorb(state, hash_size + 1, init_size, isolate); + + copy(init.ulongs, state, init_size); + return init; +} + +uint inner_loop(uint4 init, uint thread_id, __local uint* share, __global hash128_t const* g_dag, uint isolate) +{ + uint4 mix = init; + + // share init0 + if (thread_id == 0) + *share = mix.x; + barrier(CLK_LOCAL_MEM_FENCE); + uint init0 = *share; + + uint a = 0; + do + { + bool update_share = thread_id == (a/4) % THREADS_PER_HASH; + + #pragma unroll + for (uint i = 0; i != 4; ++i) + { + if (update_share) + { + uint m[4] = { mix.x, mix.y, mix.z, mix.w }; + *share = fnv(init0 ^ (a+i), m[i]) % DAG_SIZE; + } + barrier(CLK_LOCAL_MEM_FENCE); + + mix = fnv4(mix, g_dag[*share].uint4s[thread_id]); + } + } + while ((a += 4) != (ACCESSES & isolate)); + + return fnv_reduce(mix); +} + +hash32_t final_hash(hash64_t const* init, hash32_t const* mix, uint isolate) +{ + ulong state[25]; + + hash32_t hash; + uint const hash_size = countof(hash.ulongs); + uint const init_size = countof(init->ulongs); + uint const mix_size = countof(mix->ulongs); + + // keccak_256(keccak_512(header..nonce) .. mix); + copy(state, init->ulongs, init_size); + copy(state + init_size, mix->ulongs, mix_size); + keccak_f1600_no_absorb(state, init_size+mix_size, hash_size, isolate); + + // copy out + copy(hash.ulongs, state, hash_size); + return hash; +} + +hash32_t compute_hash_simple( + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong nonce, + uint isolate + ) +{ + hash64_t init = init_hash(g_header, nonce, isolate); + + hash128_t mix; + for (uint i = 0; i != countof(mix.uint4s); ++i) + { + mix.uint4s[i] = init.uint4s[i % countof(init.uint4s)]; + } + + uint mix_val = mix.uints[0]; + uint init0 = mix.uints[0]; + uint a = 0; + do + { + uint pi = fnv(init0 ^ a, mix_val) % DAG_SIZE; + uint n = (a+1) % countof(mix.uints); + + #pragma unroll + for (uint i = 0; i != countof(mix.uints); ++i) + { + mix.uints[i] = fnv(mix.uints[i], g_dag[pi].uints[i]); + mix_val = i == n ? mix.uints[i] : mix_val; + } + } + while (++a != (ACCESSES & isolate)); + + // reduce to output + hash32_t fnv_mix; + for (uint i = 0; i != countof(fnv_mix.uints); ++i) + { + fnv_mix.uints[i] = fnv_reduce(mix.uint4s[i]); + } + + return final_hash(&init, &fnv_mix, isolate); +} + +typedef union +{ + struct + { + hash64_t init; + uint pad; // avoid lds bank conflicts + }; + hash32_t mix; +} compute_hash_share; + +hash32_t compute_hash( + __local compute_hash_share* share, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + + // Compute one init hash per work item. + hash64_t init = init_hash(g_header, nonce, isolate); + + // Threads work together in this phase in groups of 8. + uint const thread_id = gid % THREADS_PER_HASH; + uint const hash_id = (gid % GROUP_SIZE) / THREADS_PER_HASH; + + hash32_t mix; + uint i = 0; + do + { + // share init with other threads + if (i == thread_id) + share[hash_id].init = init; + barrier(CLK_LOCAL_MEM_FENCE); + + uint4 thread_init = share[hash_id].init.uint4s[thread_id % (64 / sizeof(uint4))]; + barrier(CLK_LOCAL_MEM_FENCE); + + uint thread_mix = inner_loop(thread_init, thread_id, share[hash_id].mix.uints, g_dag, isolate); + + share[hash_id].mix.uints[thread_id] = thread_mix; + barrier(CLK_LOCAL_MEM_FENCE); + + if (i == thread_id) + mix = share[hash_id].mix; + barrier(CLK_LOCAL_MEM_FENCE); + } + while (++i != (THREADS_PER_HASH & isolate)); + + return final_hash(&init, &mix, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash_simple( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) +{ + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search_simple( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + uint const gid = get_global_id(0); + hash32_t hash = compute_hash_simple(g_header, g_dag, start_nonce + gid, isolate); + if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target) + { + uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1); + g_output[slot] = gid; + } +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_hash( + __global hash32_t* g_hashes, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + g_hashes[gid] = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate); +} + +__attribute__((reqd_work_group_size(GROUP_SIZE, 1, 1))) +__kernel void ethash_search( + __global volatile uint* restrict g_output, + __constant hash32_t const* g_header, + __global hash128_t const* g_dag, + ulong start_nonce, + ulong target, + uint isolate + ) +{ + __local compute_hash_share share[HASHES_PER_LOOP]; + + uint const gid = get_global_id(0); + hash32_t hash = compute_hash(share, g_header, g_dag, start_nonce + gid, isolate); + + if (as_ulong(as_uchar8(hash.ulongs[0]).s76543210) < target) + { + uint slot = min(MAX_OUTPUTS, atomic_inc(&g_output[0]) + 1); + g_output[slot] = gid; + } +} From d715f17d87498faa58f0d41dff2753c249b15fc7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 12:15:17 +0200 Subject: [PATCH 20/34] Half-finished Miner/Farm framework. --- libdevcore/Worker.cpp | 16 ++-- libdevcore/Worker.h | 6 +- libethcore/Miner.cpp | 12 +++ libethcore/Miner.h | 146 +++++++++++++++++++++++++++++ libethcore/ProofOfWork.cpp | 56 ++++++----- libethcore/ProofOfWork.h | 126 ++++++++----------------- libethereum/Client.cpp | 2 +- libethereum/Client.h | 4 +- libethereum/Farm.cpp | 12 +++ libethereum/Farm.h | 153 +++++++++++++++++++++++++++++++ libethereum/Miner.cpp | 2 +- libethereum/Miner.h | 97 +------------------- libethereum/TransactionQueue.cpp | 5 +- libethereum/TransactionQueue.h | 14 ++- libethereumx/Ethereum.h | 2 +- 15 files changed, 432 insertions(+), 221 deletions(-) create mode 100644 libethcore/Miner.cpp create mode 100644 libethcore/Miner.h create mode 100644 libethereum/Farm.cpp create mode 100644 libethereum/Farm.h diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index 175323620..bc8fe97f2 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -39,12 +39,7 @@ void Worker::startWorking() { setThreadName(m_name.c_str()); startedWorking(); - while (!m_stop) - { - if (m_idleWaitMs) - this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs)); - doWork(); - } + workLoop(); cnote << "Finishing up worker thread"; doneWorking(); })); @@ -63,3 +58,12 @@ void Worker::stopWorking() cnote << "Stopped" << m_name; } +void Worker::workLoop() +{ + while (!m_stop) + { + if (m_idleWaitMs) + this_thread::sleep_for(chrono::milliseconds(m_idleWaitMs)); + doWork(); + } +} diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 40bc118aa..6a35d6c4c 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -57,7 +57,11 @@ protected: virtual void startedWorking() {} /// Called continuously following sleep for m_idleWaitMs. - virtual void doWork() = 0; + virtual void doWork() {} + + /// Overrides doWork(); should call shouldStop() often and exit when true. + virtual void workLoop(); + bool shouldStop() const { return m_stop; } /// Called when is to be stopped, just prior to thread being joined. virtual void doneWorking() {} diff --git a/libethcore/Miner.cpp b/libethcore/Miner.cpp new file mode 100644 index 000000000..d6f15866f --- /dev/null +++ b/libethcore/Miner.cpp @@ -0,0 +1,12 @@ +#include "Miner.h" + +Miner::Miner() +{ + +} + +Miner::~Miner() +{ + +} + diff --git a/libethcore/Miner.h b/libethcore/Miner.h new file mode 100644 index 000000000..a7f17e565 --- /dev/null +++ b/libethcore/Miner.h @@ -0,0 +1,146 @@ +/* + 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 Miner.h + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "State.h" + +namespace dev +{ + +namespace eth +{ + +struct WorkPackage +{ + h256 boundary; + h256 headerHash; ///< When h256() means "pause until notified a new work package is available". + h256 seedHash; +}; + +static const WorkPackage NullWorkPackage; + +/** + * @brief Describes the progress of a mining operation. + */ +struct MiningProgress +{ + void combine(MiningProgress 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. + unsigned hashes = 0; ///< Total number of hashes computed. + unsigned ms = 0; ///< Total number of milliseconds of mining thus far. +}; + +/** + * @brief Class for hosting one or more Miners. + * @warning Must be implemented in a threadsafe manner since it will be called from multiple + * miner threads. + */ +class FarmFace +{ +public: + /** + * @brief Called from a Miner to note a WorkPackage has a solution. + * @param _p The solution. + * @param _wp The WorkPackage that the Solution is for. + * @return true iff the solution was good (implying that mining should be . + */ + virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; +}; + +/** + * @brief A miner - a member and adoptee of the Farm. + */ +class Miner +{ +public: + using ConstructionInfo = std::pair; + + Miner(ConstructionInfo const& _ci): + m_farm(_ci.first), + m_index(_ci.second) + {} + + // API FOR THE FARM TO CALL IN WITH + + void setWork(WorkPackage const& _work = WorkPackage()) + { + Guard l(x_work); + if (_work.headerHash != h256()) + kickOff(m_work); + else if (m_work.headerHash == h256() && _work.headerHash != h256()) + pause(); + m_work = _work; + } + + unsigned index() const { return m_index; } + +protected: + + // REQUIRED TO BE REIMPLEMENTED BY A SUBCLASS: + + /** + * @brief Begin working on a given work package, discarding any previous work. + * @param _work The package for which to find a solution. + */ + virtual void kickOff(WorkPackage const& _work) = 0; + + /** + * @brief No work left to be done. Pause until told to kickOff(). + */ + virtual void pause() = 0; + + // AVAILABLE FOR A SUBCLASS TO CALL: + + /** + * @brief Notes that the Miner found a solution. + * @param _s The solution. + * @return true if the solution was correct and that the miner should pause. + */ + bool submitProof(ProofOfWork::Solution const& _s) + { + if (m_farm) + { + Guard l(x_work); + return m_farm->submitProof(_s, m_work, this); + } + return true; + } + +private: + FarmFace* m_farm = nullptr; + unsigned m_index; + + Mutex x_work; + WorkPackage m_work; +}; + +} +} diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index ffa787e3e..1b3b55f4d 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -45,13 +45,38 @@ namespace dev namespace eth { -bool EthashPoW::verify(BlockInfo const& _header) +void Ethash::CPUMiner::workLoop() { - return Ethasher::verify(_header); -} + Solution solution; + + class Miner + { + public: + Miner(BlockInfo const& _header): + m_headerHash(_header.headerHash(WithoutNonce)), + m_params(Ethasher::params(_header)), + m_datasetPointer(Ethasher::get()->full(_header).data()) + {} + + inline h256 mine(uint64_t _nonce) + { + ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); +// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer); + return h256(m_ethashReturn.result, h256::ConstructFromPointer); + } + + inline h256 lastMixHash() const + { + return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); + } + + private: + ethash_return_value m_ethashReturn; + h256 m_headerHash; + ethash_params m_params; + void const* m_datasetPointer; + }; -std::pair EthashCPU::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) -{ Ethasher::Miner m(_header); std::pair ret; @@ -70,34 +95,21 @@ std::pair EthashCPU::mine(BlockInfo const& _heade double best = 1e99; // high enough to be effectively infinity :) Solution result; unsigned hashCount = 0; - for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; tryNonce++, hashCount++) + for (; !shouldStop(); tryNonce++, hashCount++) { h256 val(m.mine(tryNonce)); best = std::min(best, log2((double)(u256)val)); if (val <= boundary) { - ret.first.completed = true; - assert(Ethasher::eval(_header, (Nonce)(u64)tryNonce).value == val); - result.mixHash = m.lastMixHash(); - result.nonce = u64(tryNonce); - BlockInfo test = _header; - assignResult(result, test); - assert(verify(test)); - break; + if (submitProof(solution)) + return; } } ret.first.hashes = hashCount; ret.first.best = best; ret.second = result; - if (ret.first.completed) - { - BlockInfo test = _header; - assignResult(result, test); - assert(verify(test)); - } - - return ret; + return; } #if ETH_ETHASHCL || !ETH_TRUE diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index bd8ab58db..3ea177a9a 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -29,6 +29,7 @@ #include #include "Common.h" #include "BlockInfo.h" +#include "Miner.h" #define FAKE_DAGGER 1 @@ -51,39 +52,56 @@ struct MineInfo bool completed = false; }; -class EthashPoW +class EthashCLHook; + +class Ethash { -public: - struct Solution - { - Nonce nonce; - h256 mixHash; - }; - static bool verify(BlockInfo const& _header); - static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } +public: - virtual unsigned defaultTimeout() const { return 100; } - virtual std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) = 0; +struct Solution +{ + Nonce nonce; + h256 mixHash; }; -class EthashCPU: public EthashPoW +static bool verify(BlockInfo const& _header); +static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + +class CPUMiner: public Miner, Worker { public: - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; + CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} -protected: - Nonce m_last; + static unsigned instances() { return thread::hardware_concurrency(); } + + void kickOff(WorkPackage const& _work) override + { + stopWorking(); + m_work = _work; + startWorking(); + } + + void pause() override { stopWorking(); } + +private: + void workLoop() override; + + WorkPackage m_work; + MineInfo m_info; }; #if ETH_ETHASHCL || !ETH_TRUE -class EthashCLHook; -class EthashCL: public EthashPoW +class GPUMiner: public NewMiner { public: - EthashCL(); - ~EthashCL(); + GPUMiner(ConstructionInfo const& _ci): NewMiner(_ci) + { + + } + + static unsigned instances() { return 1; } std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; unsigned defaultTimeout() const override { return 500; } @@ -96,81 +114,15 @@ protected: std::unique_ptr m_hook; }; -using Ethash = EthashCL; #else -using Ethash = EthashCPU; -#endif -template -class ProofOfWorkEngine: public Evaluator -{ -public: - using Solution = Nonce; +using GPUMiner = CPUMiner; - static bool verify(BlockInfo const& _header) { return (bigint)(u256)Evaluator::eval(_header.headerHash(WithoutNonce), _header.nonce) <= (bigint(1) << 256) / _header.difficulty; } - inline std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true); - static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r; } - unsigned defaultTimeout() const { return 100; } - -protected: - Nonce m_last; -}; +#endif -class SHA3Evaluator -{ -public: - static h256 eval(h256 const& _root, Nonce const& _nonce) { h256 b[2] = { _root, h256(_nonce) }; return sha3(bytesConstRef((byte const*)&b[0], 64)); } }; -using SHA3ProofOfWork = ProofOfWorkEngine; - using ProofOfWork = Ethash; -template -std::pair::Solution> ProofOfWorkEngine::mine(BlockInfo const& _header, unsigned _msTimeout, bool _continue) -{ - auto headerHashWithoutNonce = _header.headerHash(WithoutNonce); - auto difficulty = _header.difficulty; - - std::pair ret; - static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()))); - Nonce::Arith s = (m_last = Nonce::random(s_eng)); - - bigint d = (bigint(1) << 256) / difficulty; - ret.first.requirement = log2((double)d); - - // 2^ 0 32 64 128 256 - // [--------*-------------------------] - // - // evaluate until we run out of time - auto startTime = std::chrono::steady_clock::now(); - double best = 1e99; // high enough to be effectively infinity :) - ProofOfWorkEngine::Solution solution; - unsigned h = 0; - for (; (std::chrono::steady_clock::now() - startTime) < std::chrono::milliseconds(_msTimeout) && _continue; s++, h++) - { - solution = (ProofOfWorkEngine::Solution)s; - auto e = (bigint)(u256)Evaluator::eval(headerHashWithoutNonce, solution); - best = std::min(best, log2((double)e)); - if (e <= d) - { - ret.first.completed = true; - break; - } - } - ret.first.hashes = h; - ret.first.best = best; - ret.second = solution; - - if (ret.first.completed) - { - BlockInfo test = _header; - assignResult(solution, test); - assert(verify(test)); - } - - return ret; -} - } } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index b2f5ff63c..e7c91c03f 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -467,7 +467,7 @@ void Client::doWork() cworkin << "WORK"; h256Set changeds; - auto maintainMiner = [&](Miner& m) + auto maintainMiner = [&](OldMiner& m) { if (m.isComplete()) { diff --git a/libethereum/Client.h b/libethereum/Client.h index ec852afd2..9af501f74 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -72,7 +72,7 @@ private: std::string m_path; }; -class RemoteMiner: public Miner +class RemoteMiner: public OldMiner { public: RemoteMiner() {} @@ -124,7 +124,7 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C */ class Client: public MinerHost, public ClientBase, Worker { - friend class Miner; + friend class OldMiner; public: /// New-style Constructor. diff --git a/libethereum/Farm.cpp b/libethereum/Farm.cpp new file mode 100644 index 000000000..639e4efcf --- /dev/null +++ b/libethereum/Farm.cpp @@ -0,0 +1,12 @@ +#include "Farm.h" + +Farm::Farm() +{ + +} + +Farm::~Farm() +{ + +} + diff --git a/libethereum/Farm.h b/libethereum/Farm.h new file mode 100644 index 000000000..a49038f0d --- /dev/null +++ b/libethereum/Farm.h @@ -0,0 +1,153 @@ +/* + 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 Miner.h + * @author Alex Leverington + * @author Gav Wood + * @date 2014 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "Miner.h" + +namespace dev +{ + +namespace eth +{ + +/** + * @brief A collective of Miners. + * Miners ask for work, then submit proofs + * @threadsafe + */ +template +class Farm: public FarmFace +{ +public: + /** + * @brief Sets the current mining mission. + * @param _bi The block (header) we wish to be mining. + */ + void setWork(BlockInfo const& _bi) + { + WriteGuard l(x_work); + m_header = _bi; + m_work.boundary = _bi.boundary(); + m_work.headerHash = _bi.headerHash(WithNonce); + m_work.seedHash = _bi.seedHash(); + ReadGuard l(x_miners); + for (auto const& m: miners) + m->setWork(m_work); + } + + /** + * @brief (Re)start miners for CPU only. + * @returns true if started properly. + */ + bool startCPU() { return start(); } + + /** + * @brief (Re)start miners for GPU only. + * @returns true if started properly. + */ + bool startGPU() { start(); } + + /** + * @brief Stop all mining activities. + */ + void stop() + { + WriteGuard l(x_miners); + m_miners.clear(); + } + + /** + * @brief Get information on the progress of mining this work package. + * @return The progress with mining so far. + */ + MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } + +protected: + // TO BE REIMPLEMENTED BY THE SUBCLASS + /** + * @brief Provides a valid header based upon that received previously with setWork(). + * @param _bi The now-valid header. + * @return true if the header was good and that the Farm should pause until more work is submitted. + */ + virtual bool submitHeader(BlockInfo const& _bi) = 0; + +private: + /** + * @brief Called from a Miner to note a WorkPackage has a solution. + * @param _p The solution. + * @param _wp The WorkPackage that the Solution is for. + * @return true iff the solution was good (implying that mining should be . + */ + bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp, NewMiner* _m) override + { + if (_wp.headerHash != m_work.headerHash) + return false; + + ProofOfWork::assignResult(_p, m_header); + if (submitHeader(m_header)) + { + ReadGuard l(x_miners); + for (std::shared_ptr const& m: m_miners) + if (m.get() != _m) + m->pause(); + m_work.headerHash = h256(); + return true; + } + return false; + } + + /** + * @brief Start a number of miners. + */ + template + bool start() + { + WriteGuard l(x_miners); + if (!m_miners.empty() && !!std::dynamic_pointer_cast(m_miners[0])) + return true; + m_miners.clear(); + m_miners.reserve(MinerType::instances()); + for (unsigned i = 0; i < MinerType::instances(); ++i) + m_miners.push_back(new MinerType(std::make_pair(this, i))); + return true; + } + + mutable SharedMutex x_miners; + std::vector> m_miners; + + mutable SharedMutex x_progress; + MineProgress m_progress; + + mutable SharedMutex x_work; + WorkPackage m_work; + BlockInfo m_header; +}; + +} +} diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index dc3d9bd9e..b386fe868 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -Miner::~Miner() {} +OldMiner::~OldMiner() {} LocalMiner::LocalMiner(MinerHost* _host, unsigned _id) { diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 86d103db5..3abf93770 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "State.h" namespace dev @@ -36,28 +37,6 @@ namespace dev namespace eth { -struct WorkPackage -{ - h256 boundary; - h256 headerHash; ///< When h256() means "pause until notified a new work package is available". - h256 seedHash; -}; - -static const WorkPackage NullWorkPackage; - -/** - * @brief Describes the progress of a mining operation. - */ -struct MineProgress -{ - 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. - unsigned hashes = 0; ///< Total number of hashes computed. - unsigned ms = 0; ///< Total number of milliseconds of mining thus far. -}; - /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple @@ -66,10 +45,6 @@ struct MineProgress class MinerHost { public: - // ============================= NEW API ============================= - virtual WorkPackage const& getWork() const { return NullWorkPackage; } - - // ============================= OLD API ============================= virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. virtual void onProgressed() {} ///< Called once some progress has been made. virtual void onComplete() {} ///< Called once a block is found. @@ -77,17 +52,17 @@ public: virtual bool turbo() const = 0; ///< @returns true iff the Miner should use GPU if possible. }; -class Miner +class OldMiner { public: - virtual ~Miner(); + virtual ~OldMiner(); virtual void noteStateChange() = 0; virtual bool isComplete() const = 0; virtual bytes const& blockData() const = 0; }; -class AsyncMiner: public Miner +class AsyncMiner: public OldMiner { public: /// Null constructor. @@ -187,69 +162,5 @@ private: std::list m_mineHistory; ///< What the history of our mining? }; -/** - * @brief A collective of Miners. - * Miners ask for work, then submit proofs - * @threadsafe - */ -class Farm: public MinerHost -{ -public: - /** - * @brief Sets the current mining mission. - * @param _bi The block (header) we wish to be mining. - */ - void setWork(BlockInfo const& _bi); - - /** - * @brief (Re)start miners for CPU only. - * @returns true if started properly. - */ - bool startCPU(); - - /** - * @brief (Re)start miners for GPU only. - * @returns true if started properly. - */ - bool startGPU(); - - /** - * @brief Stop all mining activities. - */ - void stop(); - - /** - * @brief Get information on the progress of mining this work package. - * @return The progress with mining so far. - */ - MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } - -protected: - /** - * @brief Called by a Miner to retrieve a work package. Reimplemented from MinerHost. - * @return The work package to solve. - */ - virtual WorkPackage const& getWork() const override { ReadGuard l(x_work); return m_work; } - - /** - * @brief Called from a Miner to note a WorkPackage has a solution. - * @param _p The solution. - * @param _wp The WorkPackage that the Solution is for. - * @return true iff the solution was good (implying that mining should be . - */ - virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; - -private: - mutable SharedMutex x_miners; - std::vector> m_miners; - - mutable SharedMutex x_progress; - MineProgress m_progress; - - mutable SharedMutex x_work; - WorkPackage m_work; -}; - - } } diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 38f3b9429..7c72f53e8 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) +ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallback const& _cb) { // Check if we already know this transaction. h256 h = sha3(_transactionRLP); @@ -50,7 +50,8 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP) // If valid, append to blocks. m_current[h] = t; m_known.insert(h); - + if (_cb) + m_callbacks[h] = _cb; ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); } catch (Exception const& _e) diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index 73ce24fbd..ad093b4e5 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -45,8 +46,10 @@ struct TransactionQueueChannel: public LogChannel { static const char* name() { class TransactionQueue { public: - ImportResult import(bytes const& _tx) { return import(&_tx); } - ImportResult import(bytesConstRef _tx); + using ImportCallback = std::function; + + ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx); } + ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback()); void drop(h256 _txHash); @@ -59,10 +62,11 @@ public: void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } private: - mutable boost::shared_mutex m_lock; ///< General lock. - std::set m_known; ///< Hashes of transactions in both sets. - std::map m_current; ///< Map of SHA3(tx) to tx. + mutable boost::shared_mutex m_lock; ///< General lock. + std::set m_known; ///< Hashes of transactions in both sets. + std::map m_current; ///< Map of SHA3(tx) to tx. std::multimap> m_unknown; ///< 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_callbacks; ///< Called once }; } diff --git a/libethereumx/Ethereum.h b/libethereumx/Ethereum.h index 7ff685339..15f00f4ae 100644 --- a/libethereumx/Ethereum.h +++ b/libethereumx/Ethereum.h @@ -52,7 +52,7 @@ class Client; */ class Ethereum { - friend class Miner; + friend class OldMiner; public: /// Constructor. After this, everything should be set up to go. From 87a160ab21b9316b6386c94db14aff1e0444c4a0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 15:26:37 +0200 Subject: [PATCH 21/34] GPU Miner prototyped in new API. --- libethcore/Ethasher.cpp | 134 +++++++++++++++++++------------------ libethcore/Ethasher.h | 10 ++- libethcore/ProofOfWork.cpp | 88 ++++++++++++------------ libethcore/ProofOfWork.h | 32 ++++----- 4 files changed, 135 insertions(+), 129 deletions(-) diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 6cd2504b3..2118952eb 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -39,6 +39,8 @@ using namespace chrono; using namespace dev; using namespace eth; +#define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} + Ethasher* dev::eth::Ethasher::s_this = nullptr; Ethasher::~Ethasher() @@ -59,28 +61,35 @@ void Ethasher::killCache(h256 const& _s) void const* Ethasher::light(BlockInfo const& _header) { - RecursiveGuard l(x_this); - if (_header.number > c_ethashEpochLength * 2048) - { - std::ostringstream error; - error << "block number is too high; max is " << c_ethashEpochLength * 2048 << "(was " << _header.number << ")"; - throw std::invalid_argument( error.str() ); - } + return light(_header.seedHash()); +} +void const* Ethasher::light(h256 const& _seedHash) +{ + RecursiveGuard l(x_this); if (!m_lights.count(_header.seedHash())) { - ethash_params p = params((unsigned)_header.number); - m_lights[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); + ethash_params p = params(_seedHash); + m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); } - return m_lights[_header.seedHash()]; + return m_lights[_seedHash]; } -#define IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} +bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest) +{ + return full(_header.seedHash(), _dest); +} -bytesConstRef Ethasher::full(BlockInfo const& _header) +bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) { RecursiveGuard l(x_this); - if (!m_fulls.count(_header.seedHash())) + if (m_fulls.count(_seedHash) && _dest) + { + assert(m_fulls.size() <= _dest.size()); + m_fulls.at(_seedHash).copyTo(_dest); + return; + } + if (!m_fulls.count(_seedHash)) { // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. /* if (!m_fulls.empty()) @@ -93,9 +102,9 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(c_ethashRevision, _header.seedHash()); + auto info = rlpList(c_ethashRevision, _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8)); + std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) { // memofile valid - rename. @@ -105,17 +114,26 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); - m_fulls[_header.seedHash()] = contentsNew(memoFile); - if (!m_fulls[_header.seedHash()]) + ethash_params p = params(_seedHash); + assert(!_dest || _dest.size() >= p.full_size); // must be big enough. + + bytesRef r = contentsNew(memoFile, _dest); + if (!r) { - ethash_params p = params((unsigned)_header.number); - m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); - auto c = light(_header); - ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c); - writeFile(memoFile, m_fulls[_header.seedHash()]); + // file didn't exist. + if (_dest) + // buffer was passed in - no insertion into cache nor need to allocate + r = _dest; + else + r = bytesRef(new byte[p.full_size], p.full_size); + ethash_prep_full(r, &p, light(_seedHash)); + writeFile(memoFile, r); } + if (_dest) + return _dest; + m_fulls[_seedHash] = r; } - return m_fulls[_header.seedHash()]; + return m_fulls[_seedHash]; } ethash_params Ethasher::params(BlockInfo const& _header) @@ -123,44 +141,6 @@ ethash_params Ethasher::params(BlockInfo const& _header) return params((unsigned)_header.number); } -void Ethasher::readFull(BlockInfo const& _header, void* _dest) -{ - if (!m_fulls.count(_header.seedHash())) - { - // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. -/* if (!m_fulls.empty()) - { - delete [] m_fulls.begin()->second.data(); - m_fulls.erase(m_fulls.begin()); - }*/ - - try { - boost::filesystem::create_directories(getDataDir("ethash")); - } catch (...) {} - - auto info = rlpList(c_ethashRevision, _header.seedHash()); - std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_header.seedHash().ref().cropped(0, 8)); - if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) - { - // memofile valid - rename. - boost::filesystem::rename(oldMemoFile, memoFile); - } - - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); - - ethash_params p = params((unsigned)_header.number); - bytesRef r = contentsNew(memoFile, bytesRef((byte*)_dest, p.full_size)); - if (!r) - { - auto c = light(_header); - ethash_prep_full(_dest, &p, c); - writeFile(memoFile, bytesConstRef((byte*)_dest, p.full_size)); - } - } -} - ethash_params Ethasher::params(unsigned _n) { ethash_params p; @@ -169,6 +149,27 @@ ethash_params Ethasher::params(unsigned _n) return p; } +ethash_params Ethasher::params(h256 const& _seedHash) +{ + unsigned epoch = 0; + try + { + epoch = m_seedHashes.at(_seedHash); + } + catch (...) + { + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} + if (epoch == 2048) + { + std::ostringstream error; + error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (c_ethashEpochLength * 2048); + throw std::invalid_argument(error.str()); + } + m_seedHashes[_seedHash] = epoch; + } + return params(epoch * c_ethashEpochLength); +} + bool Ethasher::verify(BlockInfo const& _header) { if (_header.number >= c_ethashEpochLength * 2048) @@ -208,13 +209,18 @@ bool Ethasher::verify(BlockInfo const& _header) } Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) +{ + return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); +} + +Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; - if (Ethasher::get()->m_fulls.count(_header.seedHash())) - ethash_compute_full(&r, Ethasher::get()->full(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + if (Ethasher::get()->m_fulls.count(_seedHash)) + ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else - ethash_compute_light(&r, Ethasher::get()->light(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + ethash_compute_light(&r, Ethasher::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h index 32622929f..f57c2f4f6 100644 --- a/libethcore/Ethasher.h +++ b/libethcore/Ethasher.h @@ -52,12 +52,14 @@ public: using FullType = void const*; LightType light(BlockInfo const& _header); - bytesConstRef full(BlockInfo const& _header); + LightType light(h256 const& _header); + bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); + bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); + static ethash_params params(BlockInfo const& _header); + static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); - void readFull(BlockInfo const& _header, void* _dest); - struct Result { h256 value; @@ -66,6 +68,7 @@ public: static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } static Result eval(BlockInfo const& _header, Nonce const& _nonce); + static Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); static bool verify(BlockInfo const& _header); class Miner @@ -103,6 +106,7 @@ private: RecursiveMutex x_this; std::map m_lights; std::map m_fulls; + std::map m_seedHashes; }; } diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index 1b3b55f4d..97488ee35 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -31,7 +31,7 @@ #include #include #include -#if ETH_ETHASHCL +#if ETH_ETHASHCL || !ETH_TRUE #include #endif #include "BlockInfo.h" @@ -134,10 +134,15 @@ public: }; */ -struct EthashCLHook: public ethash_cl_miner::search_hook +namespace dev { namespace eth { +class EthashCLHook: public ethash_cl_miner::search_hook { +public: + EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} + void abort() { + Guard l(x_all); if (m_aborted) return; // cdebug << "Attempting to abort"; @@ -147,21 +152,23 @@ struct EthashCLHook: public ethash_cl_miner::search_hook if (!m_aborted) cwarn << "Couldn't abort. Abandoning OpenCL process."; m_aborted = m_abort = false; - m_found.clear(); } - vector fetchFound() { vector ret; Guard l(x_all); std::swap(ret, m_found); return ret; } uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { - Guard l(x_all); - for (unsigned i = 0; i < _count; ++i) - m_found.push_back((Nonce)(u64)_nonces[i]); - m_aborted = true; // cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); - return true; + for (uint32_t i = 0; i < _count; ++i) + { + if (m_owner->found(_nonces[i])) + { + m_aborted = true; + return true; + } + } + return false; } virtual bool searched(uint64_t _startNonce, uint32_t _count) override @@ -180,66 +187,53 @@ protected: private: Mutex x_all; - vector m_found; uint64_t m_total; uint64_t m_last; bool m_abort = false; bool m_aborted = true; + Ethash::GPUMiner* m_owner = nullptr; }; -EthashCL::EthashCL(): - m_hook(new EthashCLHook) +} } + +Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): + Miner(_ci), + m_hook(new EthashCLHook(this)) { } -EthashCL::~EthashCL() +void Ethash::GPUMiner::report(uint64_t _nonce) { + Nonce n = (Nonce)(u64)_nonce; + Ethasher::Result r = Ethasher::eval(m_work.seedHash, m_work.headerHash, n); + if (r.value < m_work.boundary) + return submitProof(Solution{n, r.mixHash}); + return false; } -std::pair EthashCL::mine(BlockInfo const& _header, unsigned _msTimeout, bool) +void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { - if (!m_lastHeader || m_lastHeader.seedHash() != _header.seedHash()) + if (!m_miner || m_minerSeed != _work.seedHash) { if (m_miner) m_hook->abort(); m_miner.reset(new ethash_cl_miner); - auto cb = [&](void* d) { - Ethasher::get()->readFull(_header, d); - }; - m_miner->init(Ethasher::params(_header), cb, 32); + auto p = Ethasher::params(_work.seedHash); + auto cb = [&](void* d) { Ethasher::get()->readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + m_miner->init(p, cb, 32); } - if (m_lastHeader != _header) + if (m_lastWork.headerHash != _work.headerHash) { m_hook->abort(); - static std::random_device s_eng; - auto hh = _header.headerHash(WithoutNonce); - uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_header.boundary() >> 192); - m_miner->search(hh.data(), upper64OfBoundary, *m_hook); + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); + m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); } - m_lastHeader = _header; - - MineInfo mi; - Solution proof; - mi.requirement = log2((double)(u256)_header.boundary()); - mi.best = 0; - - std::this_thread::sleep_for(chrono::milliseconds(_msTimeout)); + m_work = _work; +} - mi.hashes += m_hook->fetchTotal(); - auto found = m_hook->fetchFound(); - if (!found.empty()) - { - for (auto const& n: found) - { - auto result = Ethasher::eval(_header, n); - if (result.value < _header.boundary()) - { - mi.completed = true; - proof = Solution{n, result.mixHash}; - } - } - } - return std::make_pair(mi, proof); +void Ethash::GPUMiner::pause() +{ + m_hook->abort(); } #endif diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index 3ea177a9a..f66bc77c9 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -34,7 +34,6 @@ #define FAKE_DAGGER 1 class ethash_cl_miner; -struct ethash_cl_search_hook; namespace dev { @@ -52,8 +51,6 @@ struct MineInfo bool completed = false; }; -class EthashCLHook; - class Ethash { @@ -75,6 +72,7 @@ public: static unsigned instances() { return thread::hardware_concurrency(); } +protected: void kickOff(WorkPackage const& _work) override { stopWorking(); @@ -93,25 +91,29 @@ private: #if ETH_ETHASHCL || !ETH_TRUE -class GPUMiner: public NewMiner +class EthashCLHook; + +class GPUMiner: public Miner { -public: - GPUMiner(ConstructionInfo const& _ci): NewMiner(_ci) - { + friend class EthashCLHook; - } +public: + GPUMiner(ConstructionInfo const& _ci); static unsigned instances() { return 1; } - std::pair mine(BlockInfo const& _header, unsigned _msTimeout = 100, bool _continue = true) override; - unsigned defaultTimeout() const override { return 500; } - protected: - Nonce m_last; - BlockInfo m_lastHeader; - Nonce m_mined; - std::unique_ptr m_miner; + void kickOff(WorkPackage const& _work) override; + void pause() override; + +private: + void report(uint64_t _nonce); + std::unique_ptr m_hook; + std::unique_ptr m_miner; + h256 m_minerSeed; + WorkPackage m_lastWork; ///< Work loaded into m_miner. + MineInfo m_info; }; #else From c1045d47117704eb30616a42ae733bd8dbfbfd76 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 12 Apr 2015 20:17:33 +0200 Subject: [PATCH 22/34] Refactor just about everything important in the core. TODO: make compile :-) --- libethcore/Common.cpp | 2 - libethcore/Common.h | 37 +++ libethcore/Ethash.cpp | 241 ++++++++++++++++ libethcore/Ethash.h | 128 +++++++++ libethcore/{Ethasher.cpp => EthashAux.cpp} | 144 ++++------ libethcore/EthashAux.h | 63 +++++ libethcore/Ethasher.h | 113 -------- libethcore/Miner.cpp | 12 - libethcore/Miner.h | 38 ++- libethcore/ProofOfWork.cpp | 220 +-------------- libethcore/ProofOfWork.h | 109 +------- libethereum/BlockChain.cpp | 29 +- libethereum/BlockChain.h | 5 +- libethereum/BlockQueue.cpp | 1 + libethereum/BlockQueue.h | 3 + libethereum/Client.cpp | 309 +++++++++------------ libethereum/Client.h | 67 +++-- libethereum/ClientBase.h | 2 - libethereum/Farm.cpp | 12 - libethereum/Farm.h | 41 +-- libethereum/State.cpp | 14 - libethereum/State.h | 31 +-- libethereum/TransactionQueue.cpp | 1 + libethereum/TransactionQueue.h | 6 +- 24 files changed, 801 insertions(+), 827 deletions(-) create mode 100644 libethcore/Ethash.cpp create mode 100644 libethcore/Ethash.h rename libethcore/{Ethasher.cpp => EthashAux.cpp} (60%) create mode 100644 libethcore/EthashAux.h delete mode 100644 libethcore/Ethasher.h diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index 572ade3e2..f0e749aaa 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -22,7 +22,6 @@ #include "Common.h" #include #include -#include "Ethasher.h" #include "Exceptions.h" using namespace std; using namespace dev; @@ -33,7 +32,6 @@ namespace dev namespace eth { -const unsigned c_ethashVersion = c_ethashRevision; const unsigned c_protocolVersion = 60; const unsigned c_minorProtocolVersion = 0; const unsigned c_databaseBaseVersion = 9; diff --git a/libethcore/Common.h b/libethcore/Common.h index ec826d2d1..b5291648b 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -96,5 +96,42 @@ enum class ImportResult BadChain }; +class Signal; + +class Handler +{ +public: + Handler() = default; + Handler(Handler const&) = delete; + ~Handler() { reset(); } + + Handler& operator=(Handler const& _h) = delete; + + void reset(); + +private: + Handler(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} + + unsigned m_i = 0; + Signal* m_s = nullptr; +}; + +/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. +class Signal +{ +public: + using Callback = std::function; + using Callbacks = std::vector; + + Handler add(Callback const& _h) { auto n = m_onReady.size() ? m_onReady.rbegin()->first + 1 : 0; m_onReady[n] = _h; return Handler(n, this); } + + void operator()() { for (auto const& f: m_fire) f.second(); } + +private: + std::map m_fire; +}; + +inline void Handler::reset() { if (m_s) m_s->m_fire->erase(m_i); m_s = nullptr; } + } } diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp new file mode 100644 index 000000000..82e349b4c --- /dev/null +++ b/libethcore/Ethash.cpp @@ -0,0 +1,241 @@ +/* + 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 Ethash.cpp + * @author Gav Wood + * @date 2014 + */ + +#include "Ethash.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ETH_ETHASHCL || !ETH_TRUE +#include +#endif +#include "BlockInfo.h" +#include "EthashAux.h" +using namespace std; +using namespace std::chrono; + +namespace dev +{ +namespace eth +{ + +const Ethash::WorkPackage Ethash::NullWorkPackage; + +std::string Ethash::name() +{ + return "Ethash"; +} + +unsigned Ethash::revision() +{ + return ETHASH_REVISION; +} + +void Ethash::prep(BlockInfo const& _header) +{ + if (_header.number % ETHASH_EPOCH_LENGTH == 1) + EthashAux::full(bi); +} + +bool Ethash::preVerify(BlockInfo const& _header) +{ + if (_header.number >= ETHASH_EPOCH_LENGTH * 2048) + return false; + + h256 boundary = u256((bigint(1) << 256) / _header.difficulty); + + return ethash_quick_check_difficulty( + _header.headerHash(WithoutNonce).data(), + (uint64_t)(u64)_header.nonce, + _header.mixHash.data(), + boundary.data()); +} + +bool Ethash::verify(BlockInfo const& _header) +{ + bool pre = preVerify(_header); +#if !ETH_DEBUG + if (!pre) + return false; +#endif + + h256 boundary = u256((bigint(1) << 256) / _header.difficulty); + auto result = eval(_header); + bool slow = result.value <= boundary && result.mixHash == _header.mixHash; + +#if ETH_DEBUG || !ETH_TRUE + if (!pre && slow) + { + cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false."; + cwarn << "headerHash:" << _header.headerHash(WithoutNonce); + cwarn << "nonce:" << _header.nonce; + cwarn << "mixHash:" << _header.mixHash; + cwarn << "difficulty:" << _header.difficulty; + cwarn << "boundary:" << boundary; + cwarn << "result.value:" << result.value; + cwarn << "result.mixHash:" << result.mixHash; + } +#endif + + return slow; +} + +void Ethash::CPUMiner::workLoop() +{ + auto tid = std::this_thread::get_id(); + static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); + + uint64_t tryNonce = Nonce::random(s_eng); + ethash_return_value ethashReturn; + + auto p = Ethash::params(m_work.seedHash); + void const* dagPointer = Ethash::full(m_work.headerHash).data(); + uint8_t const* headerHashPointer = m_work.headerHash.data(); + h256 boundary = m_work.boundary(); + unsigned hashCount = 0; + for (; !shouldStop(); tryNonce++, hashCount++) + { + ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); + h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); + if (value <= boundary && submitProof(Solution{value, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) + break; + } +} + +#if ETH_ETHASHCL || !ETH_TRUE + +namespace dev { namespace eth { + +class EthashCLHook: public ethash_cl_miner::search_hook +{ +public: + EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} + + void abort() + { + Guard l(x_all); + if (m_aborted) + return; +// cdebug << "Attempting to abort"; + m_abort = true; + for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) + std::this_thread::sleep_for(chrono::milliseconds(30)); + if (!m_aborted) + cwarn << "Couldn't abort. Abandoning OpenCL process."; + m_aborted = m_abort = false; + } + + uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } + +protected: + virtual bool found(uint64_t const* _nonces, uint32_t _count) override + { +// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); + for (uint32_t i = 0; i < _count; ++i) + { + if (m_owner->found(_nonces[i])) + { + m_aborted = true; + return true; + } + } + return false; + } + + virtual bool searched(uint64_t _startNonce, uint32_t _count) override + { + Guard l(x_all); +// cdebug << "Searched" << _count << "from" << _startNonce; + m_total += _count; + m_last = _startNonce + _count; + if (m_abort) + { + m_aborted = true; + return true; + } + return false; + } + +private: + Mutex x_all; + uint64_t m_total; + uint64_t m_last; + bool m_abort = false; + bool m_aborted = true; + Ethash::GPUMiner* m_owner = nullptr; +}; + +} } + +Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): + Miner(_ci), + m_hook(new EthashCLHook(this)) +{ +} + +void Ethash::GPUMiner::report(uint64_t _nonce) +{ + Nonce n = (Nonce)(u64)_nonce; + Result r = Ethash::eval(m_work.seedHash, m_work.headerHash, n); + if (r.value < m_work.boundary) + return submitProof(Solution{n, r.mixHash}); + return false; +} + +void Ethash::GPUMiner::kickOff(WorkPackage const& _work) +{ + if (!m_miner || m_minerSeed != _work.seedHash) + { + if (m_miner) + m_hook->abort(); + m_miner.reset(new ethash_cl_miner); + auto p = Ethash::params(_work.seedHash); + auto cb = [&](void* d) { EthashAux::readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + m_miner->init(p, cb, 32); + } + if (m_lastWork.headerHash != _work.headerHash) + { + m_hook->abort(); + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); + m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); + } + m_work = _work; +} + +void Ethash::GPUMiner::pause() +{ + m_hook->abort(); +} + +#endif + +} +} diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h new file mode 100644 index 000000000..bdd6bf6c5 --- /dev/null +++ b/libethcore/Ethash.h @@ -0,0 +1,128 @@ +/* + 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 Ethash.h + * @author Gav Wood + * @date 2014 + * + * A proof of work algorithm. + */ + +#pragma once + +#include +#include +#include +#include "Common.h" +#include "BlockInfo.h" +#include "Miner.h" + +class ethash_cl_miner; + +namespace dev +{ +namespace eth +{ + +class Ethash +{ +public: + struct Solution + { + Nonce nonce; + h256 mixHash; + }; + + struct Result + { + h256 value; + h256 mixHash; + }; + + struct WorkPackage + { + h256 boundary; + h256 headerHash; ///< When h256() means "pause until notified a new work package is available". + h256 seedHash; + }; + + static const WorkPackage NullWorkPackage; + + static std::string name(); + static unsigned revision(); + static bool verify(BlockInfo const& _header); + static bool preVerify(BlockInfo const& _header); + static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } + static void prep(BlockInfo const& _header); + + class CPUMiner: public Miner, Worker + { + public: + CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} + + static unsigned instances() { return thread::hardware_concurrency(); } + + protected: + void kickOff(WorkPackage const& _work) override + { + stopWorking(); + m_work = _work; + startWorking(); + } + + void pause() override { stopWorking(); } + + private: + void workLoop() override; + + WorkPackage m_work; + MineInfo m_info; + }; + +#if ETH_ETHASHCL || !ETH_TRUE + class EthashCLHook; + class GPUMiner: public Miner + { + friend class EthashCLHook; + + public: + GPUMiner(ConstructionInfo const& _ci); + + static unsigned instances() { return 1; } + + protected: + void kickOff(WorkPackage const& _work) override; + void pause() override; + + private: + void report(uint64_t _nonce); + + std::unique_ptr m_hook; + std::unique_ptr m_miner; + h256 m_minerSeed; + WorkPackage m_lastWork; ///< Work loaded into m_miner. + MineInfo m_info; + }; +#else + using GPUMiner = CPUMiner; +#endif +}; + +using ProofOfWork = Ethash; +using Solution = Ethash::Solution; + +} +} diff --git a/libethcore/Ethasher.cpp b/libethcore/EthashAux.cpp similarity index 60% rename from libethcore/Ethasher.cpp rename to libethcore/EthashAux.cpp index 2118952eb..a6143e264 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/EthashAux.cpp @@ -14,11 +14,13 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file Ethasher.cpp +/** @file EthashAux.cpp * @author Gav Wood * @date 2014 */ +#include "EthashAux.h" + #include #include #include @@ -33,7 +35,6 @@ #include #include #include "BlockInfo.h" -#include "Ethasher.h" using namespace std; using namespace chrono; using namespace dev; @@ -41,15 +42,50 @@ using namespace eth; #define ETH_IGNORE_EXCEPTIONS(X) try { X; } catch (...) {} -Ethasher* dev::eth::Ethasher::s_this = nullptr; +EthashAux* dev::eth::EthashAux::s_this = nullptr; -Ethasher::~Ethasher() +EthashAux::~EthashAux() { while (!m_lights.empty()) killCache(m_lights.begin()->first); } -void Ethasher::killCache(h256 const& _s) +ethash_params EthashAux::params(BlockInfo const& _header) +{ + return params((unsigned)_header.number); +} + +ethash_params EthashAux::params(unsigned _n) +{ + ethash_params p; + p.cache_size = ethash_get_cachesize(_n); + p.full_size = ethash_get_datasize(_n); + return p; +} + +ethash_params EthashAux::params(h256 const& _seedHash) +{ + RecursiveGuard l(get()->x_this); + unsigned epoch = 0; + try + { + epoch = get()->m_seedHashes.at(_seedHash); + } + catch (...) + { + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} + if (epoch == 2048) + { + std::ostringstream error; + error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); + throw std::invalid_argument(error.str()); + } + get()->m_seedHashes[_seedHash] = epoch; + } + return params(epoch * ETHASH_EPOCH_LENGTH); +} + +void EthashAux::killCache(h256 const& _s) { RecursiveGuard l(x_this); if (m_lights.count(_s)) @@ -59,12 +95,12 @@ void Ethasher::killCache(h256 const& _s) } } -void const* Ethasher::light(BlockInfo const& _header) +void const* EthashAux::light(BlockInfo const& _header) { return light(_header.seedHash()); } -void const* Ethasher::light(h256 const& _seedHash) +void const* EthashAux::light(h256 const& _seedHash) { RecursiveGuard l(x_this); if (!m_lights.count(_header.seedHash())) @@ -75,12 +111,12 @@ void const* Ethasher::light(h256 const& _seedHash) return m_lights[_seedHash]; } -bytesConstRef Ethasher::full(BlockInfo const& _header, bytesRef _dest) +bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest) { return full(_header.seedHash(), _dest); } -bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) +bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) { RecursiveGuard l(x_this); if (m_fulls.count(_seedHash) && _dest) @@ -102,9 +138,9 @@ bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(c_ethashRevision, _seedHash); + auto info = rlpList(revision(), _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; - std::string memoFile = getDataDir("ethash") + "/full-R" + toString(c_ethashRevision) + "-" + toHex(_seedHash.ref().cropped(0, 8)); + std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) { // memofile valid - rename. @@ -136,91 +172,19 @@ bytesConstRef Ethasher::full(h256 const& _seedHash, bytesRef _dest) return m_fulls[_seedHash]; } -ethash_params Ethasher::params(BlockInfo const& _header) -{ - return params((unsigned)_header.number); -} - -ethash_params Ethasher::params(unsigned _n) -{ - ethash_params p; - p.cache_size = ethash_get_cachesize(_n); - p.full_size = ethash_get_datasize(_n); - return p; -} - -ethash_params Ethasher::params(h256 const& _seedHash) -{ - unsigned epoch = 0; - try - { - epoch = m_seedHashes.at(_seedHash); - } - catch (...) - { - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} - if (epoch == 2048) - { - std::ostringstream error; - error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (c_ethashEpochLength * 2048); - throw std::invalid_argument(error.str()); - } - m_seedHashes[_seedHash] = epoch; - } - return params(epoch * c_ethashEpochLength); -} - -bool Ethasher::verify(BlockInfo const& _header) -{ - if (_header.number >= c_ethashEpochLength * 2048) - return false; - - h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - - bool quick = ethash_quick_check_difficulty( - _header.headerHash(WithoutNonce).data(), - (uint64_t)(u64)_header.nonce, - _header.mixHash.data(), - boundary.data()); - -#if !ETH_DEBUG - if (!quick) - return false; -#endif - - auto result = eval(_header); - bool slow = result.value <= boundary && result.mixHash == _header.mixHash; - -#if ETH_DEBUG - if (!quick && slow) - { - cwarn << "WARNING: evaluated result gives true whereas ethash_quick_check_difficulty gives false."; - cwarn << "headerHash:" << _header.headerHash(WithoutNonce); - cwarn << "nonce:" << _header.nonce; - cwarn << "mixHash:" << _header.mixHash; - cwarn << "difficulty:" << _header.difficulty; - cwarn << "boundary:" << boundary; - cwarn << "result.value:" << result.value; - cwarn << "result.mixHash:" << result.mixHash; - } -#endif - - return slow; -} - -Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) +Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) { return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); } -Ethasher::Result Ethasher::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) +Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) { - auto p = Ethasher::params(_header); + auto p = EthashAux::params(_header); ethash_return_value r; - if (Ethasher::get()->m_fulls.count(_seedHash)) - ethash_compute_full(&r, Ethasher::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); + if (EthashAux::get()->m_fulls.count(_seedHash)) + ethash_compute_full(&r, EthashAux::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else - ethash_compute_light(&r, Ethasher::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); -// cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); + ethash_compute_light(&r, EthashAux::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); +// cdebug << "EthashAux::eval sha3(cache):" << sha3(EthashAux::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h new file mode 100644 index 000000000..bfd01a594 --- /dev/null +++ b/libethcore/EthashAux.h @@ -0,0 +1,63 @@ +/* + 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 EthashAux.cpp + * @author Gav Wood + * @date 2014 + */ + +#include +#include "Ethash.h" + +namespace dev +{ +namespace eth{ + +class EthashAux +{ + EthashAux() {} + ~EthashAux(); + + static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; } + + using LightType = void const*; + using FullType = void const*; + + static ethash_params params(BlockInfo const& _header); + static ethash_params params(h256 const& _seedHash); + static ethash_params params(unsigned _n); + static LightType light(BlockInfo const& _header); + static LightType light(h256 const& _header); + static bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); + static bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); + + static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } + static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce); + static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); + +private: + void killCache(h256 const& _s); + + static Ethash* s_this; + RecursiveMutex x_this; + + std::map m_lights; + std::map m_fulls; + std::map m_seedHashes; +}; + +} +} diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h deleted file mode 100644 index f57c2f4f6..000000000 --- a/libethcore/Ethasher.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - 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 Ethasher.h - * @author Gav Wood - * @date 2014 - * - * ProofOfWork algorithm. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include // TODO: REMOVE once everything merged into this class and an opaque API can be provided. -static const unsigned c_ethashRevision = ETHASH_REVISION; -static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH; -#include "Common.h" -#include "BlockInfo.h" - -namespace dev -{ -namespace eth -{ - -class Ethasher -{ -public: - Ethasher() {} - ~Ethasher(); - - static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } - - using LightType = void const*; - using FullType = void const*; - - LightType light(BlockInfo const& _header); - LightType light(h256 const& _header); - bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); - bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); - - static ethash_params params(BlockInfo const& _header); - static ethash_params params(h256 const& _seedHash); - static ethash_params params(unsigned _n); - - struct Result - { - h256 value; - h256 mixHash; - }; - - static Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } - static Result eval(BlockInfo const& _header, Nonce const& _nonce); - static Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); - static bool verify(BlockInfo const& _header); - - class Miner - { - public: - Miner(BlockInfo const& _header): - m_headerHash(_header.headerHash(WithoutNonce)), - m_params(Ethasher::params(_header)), - m_datasetPointer(Ethasher::get()->full(_header).data()) - {} - - inline h256 mine(uint64_t _nonce) - { - ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); -// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer); - return h256(m_ethashReturn.result, h256::ConstructFromPointer); - } - - inline h256 lastMixHash() const - { - return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); - } - - private: - ethash_return_value m_ethashReturn; - h256 m_headerHash; - ethash_params m_params; - void const* m_datasetPointer; - }; - -private: - void killCache(h256 const& _s); - - static Ethasher* s_this; - RecursiveMutex x_this; - std::map m_lights; - std::map m_fulls; - std::map m_seedHashes; -}; - -} -} diff --git a/libethcore/Miner.cpp b/libethcore/Miner.cpp index d6f15866f..e69de29bb 100644 --- a/libethcore/Miner.cpp +++ b/libethcore/Miner.cpp @@ -1,12 +0,0 @@ -#include "Miner.h" - -Miner::Miner() -{ - -} - -Miner::~Miner() -{ - -} - diff --git a/libethcore/Miner.h b/libethcore/Miner.h index a7f17e565..e7157e660 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -15,9 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Miner.h - * @author Alex Leverington * @author Gav Wood - * @date 2014 + * @date 2015 */ #pragma once @@ -28,7 +27,6 @@ #include #include #include -#include "State.h" namespace dev { @@ -36,15 +34,17 @@ namespace dev namespace eth { -struct WorkPackage +struct MineInfo { - h256 boundary; - h256 headerHash; ///< When h256() means "pause until notified a new work package is available". - h256 seedHash; + MineInfo() = default; + MineInfo(bool _completed): completed(_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; + unsigned hashes = 0; + bool completed = false; }; -static const WorkPackage NullWorkPackage; - /** * @brief Describes the progress of a mining operation. */ @@ -58,30 +58,40 @@ struct MiningProgress unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; +template class Miner; + /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple * miner threads. */ -class FarmFace +template class FarmFace { public: + using WorkPackage = typename PoW::WorkPackage; + using Solution = typename PoW::Solution; + using Miner = Miner; + /** * @brief Called from a Miner to note a WorkPackage has a solution. * @param _p The solution. * @param _wp The WorkPackage that the Solution is for. + * @param _finder The miner that found it. * @return true iff the solution was good (implying that mining should be . */ - virtual bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp) = 0; + virtual bool submitProof(Solution const& _p, WorkPackage const& _wp, Miner* _finder) = 0; }; /** * @brief A miner - a member and adoptee of the Farm. */ -class Miner +template class Miner { public: - using ConstructionInfo = std::pair; + using ConstructionInfo = std::pair*, unsigned>; + using WorkPackage = typename PoW::WorkPackage; + using Solution = typename PoW::Solution; + using FarmFace = FarmFace; Miner(ConstructionInfo const& _ci): m_farm(_ci.first), @@ -124,7 +134,7 @@ protected: * @param _s The solution. * @return true if the solution was correct and that the miner should pause. */ - bool submitProof(ProofOfWork::Solution const& _s) + bool submitProof(Solution const& _s) { if (m_farm) { diff --git a/libethcore/ProofOfWork.cpp b/libethcore/ProofOfWork.cpp index 97488ee35..ec910f7f2 100644 --- a/libethcore/ProofOfWork.cpp +++ b/libethcore/ProofOfWork.cpp @@ -19,224 +19,6 @@ * @date 2014 */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if ETH_ETHASHCL || !ETH_TRUE -#include -#endif -#include "BlockInfo.h" -#include "Ethasher.h" #include "ProofOfWork.h" using namespace std; -using namespace std::chrono; - -namespace dev -{ -namespace eth -{ - -void Ethash::CPUMiner::workLoop() -{ - Solution solution; - - class Miner - { - public: - Miner(BlockInfo const& _header): - m_headerHash(_header.headerHash(WithoutNonce)), - m_params(Ethasher::params(_header)), - m_datasetPointer(Ethasher::get()->full(_header).data()) - {} - - inline h256 mine(uint64_t _nonce) - { - ethash_compute_full(&m_ethashReturn, m_datasetPointer, &m_params, m_headerHash.data(), _nonce); -// cdebug << "Ethasher::mine hh:" << m_headerHash << "nonce:" << (Nonce)(u64)_nonce << " => " << h256(m_ethashReturn.result, h256::ConstructFromPointer); - return h256(m_ethashReturn.result, h256::ConstructFromPointer); - } - - inline h256 lastMixHash() const - { - return h256(m_ethashReturn.mix_hash, h256::ConstructFromPointer); - } - - private: - ethash_return_value m_ethashReturn; - h256 m_headerHash; - ethash_params m_params; - void const* m_datasetPointer; - }; - - Ethasher::Miner m(_header); - - std::pair ret; - auto tid = std::this_thread::get_id(); - static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); - uint64_t tryNonce = (uint64_t)(u64)(m_last = Nonce::random(s_eng)); - - h256 boundary = _header.boundary(); - ret.first.requirement = log2((double)(u256)boundary); - - // 2^ 0 32 64 128 256 - // [--------*-------------------------] - // - // evaluate until we run out of time - auto startTime = std::chrono::steady_clock::now(); - double best = 1e99; // high enough to be effectively infinity :) - Solution result; - unsigned hashCount = 0; - for (; !shouldStop(); tryNonce++, hashCount++) - { - h256 val(m.mine(tryNonce)); - best = std::min(best, log2((double)(u256)val)); - if (val <= boundary) - { - if (submitProof(solution)) - return; - } - } - ret.first.hashes = hashCount; - ret.first.best = best; - ret.second = result; - - return; -} - -#if ETH_ETHASHCL || !ETH_TRUE - -/* -class ethash_cl_miner -{ -public: - struct search_hook - { - // reports progress, return true to abort - virtual bool found(uint64_t const* nonces, uint32_t count) = 0; - virtual bool searched(uint64_t start_nonce, uint32_t count) = 0; - }; - - ethash_cl_miner(); - - bool init(ethash_params const& params, const uint8_t seed[32], unsigned workgroup_size = 64); - - void hash(uint8_t* ret, uint8_t const* header, uint64_t nonce, unsigned count); - void search(uint8_t const* header, uint64_t target, search_hook& hook); -}; -*/ - -namespace dev { namespace eth { -class EthashCLHook: public ethash_cl_miner::search_hook -{ -public: - EthashCLHook(Ethash::GPUMiner* _owner): m_owner(_owner) {} - - void abort() - { - Guard l(x_all); - if (m_aborted) - return; -// cdebug << "Attempting to abort"; - m_abort = true; - for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) - std::this_thread::sleep_for(chrono::milliseconds(30)); - if (!m_aborted) - cwarn << "Couldn't abort. Abandoning OpenCL process."; - m_aborted = m_abort = false; - } - - uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } - -protected: - virtual bool found(uint64_t const* _nonces, uint32_t _count) override - { -// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); - for (uint32_t i = 0; i < _count; ++i) - { - if (m_owner->found(_nonces[i])) - { - m_aborted = true; - return true; - } - } - return false; - } - - virtual bool searched(uint64_t _startNonce, uint32_t _count) override - { - Guard l(x_all); -// cdebug << "Searched" << _count << "from" << _startNonce; - m_total += _count; - m_last = _startNonce + _count; - if (m_abort) - { - m_aborted = true; - return true; - } - return false; - } - -private: - Mutex x_all; - uint64_t m_total; - uint64_t m_last; - bool m_abort = false; - bool m_aborted = true; - Ethash::GPUMiner* m_owner = nullptr; -}; - -} } - -Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): - Miner(_ci), - m_hook(new EthashCLHook(this)) -{ -} - -void Ethash::GPUMiner::report(uint64_t _nonce) -{ - Nonce n = (Nonce)(u64)_nonce; - Ethasher::Result r = Ethasher::eval(m_work.seedHash, m_work.headerHash, n); - if (r.value < m_work.boundary) - return submitProof(Solution{n, r.mixHash}); - return false; -} - -void Ethash::GPUMiner::kickOff(WorkPackage const& _work) -{ - if (!m_miner || m_minerSeed != _work.seedHash) - { - if (m_miner) - m_hook->abort(); - m_miner.reset(new ethash_cl_miner); - auto p = Ethasher::params(_work.seedHash); - auto cb = [&](void* d) { Ethasher::get()->readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; - m_miner->init(p, cb, 32); - } - if (m_lastWork.headerHash != _work.headerHash) - { - m_hook->abort(); - uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); - m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); - } - m_work = _work; -} - -void Ethash::GPUMiner::pause() -{ - m_hook->abort(); -} - -#endif - -} -} +using namespace dev; diff --git a/libethcore/ProofOfWork.h b/libethcore/ProofOfWork.h index f66bc77c9..764207aef 100644 --- a/libethcore/ProofOfWork.h +++ b/libethcore/ProofOfWork.h @@ -18,112 +18,29 @@ * @author Gav Wood * @date 2014 * - * ProofOfWork algorithm. Or not. + * Determines the PoW algorithm. */ #pragma once -#include -#include -#include -#include -#include "Common.h" -#include "BlockInfo.h" -#include "Miner.h" - -#define FAKE_DAGGER 1 - -class ethash_cl_miner; +#include "Ethash.h" namespace dev { namespace eth { -struct MineInfo -{ - MineInfo() = default; - MineInfo(bool _completed): completed(_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; - unsigned hashes = 0; - bool completed = false; -}; - -class Ethash -{ - -public: - -struct Solution -{ - Nonce nonce; - h256 mixHash; -}; - -static bool verify(BlockInfo const& _header); -static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } - -class CPUMiner: public Miner, Worker -{ -public: - CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} - - static unsigned instances() { return thread::hardware_concurrency(); } - -protected: - void kickOff(WorkPackage const& _work) override - { - stopWorking(); - m_work = _work; - startWorking(); - } - - void pause() override { stopWorking(); } - -private: - void workLoop() override; - - WorkPackage m_work; - MineInfo m_info; -}; - -#if ETH_ETHASHCL || !ETH_TRUE - -class EthashCLHook; - -class GPUMiner: public Miner -{ - friend class EthashCLHook; - -public: - GPUMiner(ConstructionInfo const& _ci); - - static unsigned instances() { return 1; } - -protected: - void kickOff(WorkPackage const& _work) override; - void pause() override; - -private: - void report(uint64_t _nonce); - - std::unique_ptr m_hook; - std::unique_ptr m_miner; - h256 m_minerSeed; - WorkPackage m_lastWork; ///< Work loaded into m_miner. - MineInfo m_info; -}; - -#else - -using GPUMiner = CPUMiner; - -#endif - -}; - +/** + * The proof of work algorithm base type. + * + * Must implement a basic templated interface, including: + * typename Result + * typename Solution + * typename CPUMiner + * typename GPUMiner + * void assignResult(BlockInfo&, Result) + * and a few others. TODO + */ using ProofOfWork = Ethash; } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index bf2bce4fe..0ff0a3628 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -231,8 +231,7 @@ void BlockChain::rebuild(std::string const& _path, std::function(h256(u256(d)), m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB).value); BlockInfo bi(b); - if (bi.number % c_ethashEpochLength == 1) - Ethasher::get()->full(bi); + ProofOfWork::prep(bi); if (bi.parentHash != lastHash) { @@ -307,14 +306,8 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st try { auto r = import(block, _stateDB); - bool isOld = true; - for (auto const& h: r.first) - if (h == r.second) - isOld = false; - else if (isOld) - dead.push_back(h); - else - fresh.push_back(h); + fresh += r.first; + dead += r.second; } catch (UnknownParent) { @@ -334,7 +327,7 @@ tuple BlockChain::sync(BlockQueue& _bq, OverlayDB const& _st return make_tuple(fresh, dead, _bq.doneDrain(badBlocks)); } -pair BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force) noexcept +ImportRoute BlockChain::attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force) noexcept { try { @@ -347,7 +340,7 @@ pair BlockChain::attemptImport(bytes const& _block, OverlayDB const } } -pair BlockChain::import(bytes const& _block, OverlayDB const& _db, Aversion _force) +ImportRoute BlockChain::import(bytes const& _block, OverlayDB const& _db, Aversion _force) { //@tidy This is a behemoth of a method - could do to be split into a few smaller ones. @@ -626,7 +619,17 @@ pair BlockChain::import(bytes const& _block, OverlayDB const& _db, cnote << "checkBest:" << checkBest; #endif - return make_pair(route, common); + h256s fresh; + h256s dead; + bool isOld = true; + for (auto const& h: route) + if (h == common) + isOld = false; + else if (isOld) + dead.push_back(h); + else + fresh.push_back(h); + return make_pair(fresh, dead); } void BlockChain::clearBlockBlooms(unsigned _begin, unsigned _end) diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index 765e00b03..cdff566fb 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -68,6 +68,7 @@ ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; using UncleHashes = h256s; +using ImportRoute = std::pair; enum { ExtraDetails = 0, @@ -108,11 +109,11 @@ public: /// Attempt to import the given block directly into the CanonBlockChain and sync with the state DB. /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. - std::pair attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks) noexcept; + ImportRoute attemptImport(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks) noexcept; /// Import block into disk-backed DB /// @returns the block hashes of any blocks that came into/went out of the canonical block chain. - std::pair import(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks); + ImportRoute import(bytes const& _block, OverlayDB const& _stateDB, Aversion _force = Aversion::AvoidOldBlocks); /// Returns true if the given block is known (though not necessarily a part of the canon chain). bool isKnown(h256 const& _hash) const; diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index 6f8c64827..e9dd99cd1 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -102,6 +102,7 @@ ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) m_readySet.insert(h); noteReadyWithoutWriteGuard(h); + m_onReady(); return ImportResult::Success; } } diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index ce0582db2..f5cdf7ab5 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -83,6 +83,8 @@ public: /// Get some infomration on the current status. BlockQueueStatus status() const { ReadGuard l(m_lock); return BlockQueueStatus{m_ready.size(), m_future.size(), m_unknown.size(), m_knownBad.size()}; } + template Handler onReady(T const& _t) { return m_onReady.add(_t); } + private: void noteReadyWithoutWriteGuard(h256 _b); void notePresentWithoutWriteGuard(bytesConstRef _block); @@ -95,6 +97,7 @@ private: std::multimap> m_unknown; ///< For transactions that have an unknown parent; we map their parent hash to the block stuff, and insert once the block appears. std::multimap m_future; ///< Set of blocks that are not yet valid. std::set m_knownBad; ///< Set of blocks that we know will never be valid. + Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. }; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index e7c91c03f..15a7c9bec 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -117,7 +117,7 @@ void BasicGasPricer::update(BlockChain const& _bc) } } -Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): +Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): Worker("eth"), m_vc(_dbPath), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), @@ -126,14 +126,14 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_preMine(m_stateDB, BaseState::CanonGenesis), m_postMine(m_stateDB) { + m_tqReady = m_tq->onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq->onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_gp->update(m_bc); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); - if (_miners > -1) - setMiningThreads(_miners); - else - setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); @@ -142,7 +142,7 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for startWorking(); } -Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId, int _miners): +Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string const& _dbPath, WithExisting _forceAction, u256 _networkId): Worker("eth"), m_vc(_dbPath), m_bc(_dbPath, max(m_vc.action(), _forceAction), [](unsigned d, unsigned t){ cerr << "REVISING BLOCKCHAIN: Processed " << d << " of " << t << "...\r"; }), @@ -151,14 +151,14 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string c m_preMine(m_stateDB), m_postMine(m_stateDB) { + m_tq->onReady([=](){ this->onTransactionQueueReady(); }); + m_bq->onReady([=](){ this->onBlockQueueReady(); }); + m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_gp->update(m_bc); m_host = _extNet->registerCapability(new EthereumHost(m_bc, m_tq, m_bq, _networkId)); - if (_miners > -1) - setMiningThreads(_miners); - else - setMiningThreads(); if (_dbPath.size()) Defaults::setDBPath(_dbPath); m_vc.setOk(); @@ -229,8 +229,6 @@ void Client::killChain() doWork(); - setMiningThreads(0); - startWorking(); if (wasMining) startMining(); @@ -271,26 +269,6 @@ static string filtersToString(T const& _fs) return ret.str(); } -void Client::noteChanged(h256Set const& _filters) -{ - Guard l(x_filtersWatches); - if (_filters.size()) - cnote << "noteChanged(" << filtersToString(_filters) << ")"; - // accrue all changes left in each filter into the watches. - for (auto& w: m_watches) - if (_filters.count(w.second.id)) - { - cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???"); - if (m_filters.count(w.second.id)) // Normal filtering watch - w.second.changes += m_filters.at(w.second.id).changes; - else // Special ('pending'/'latest') watch - w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); - } - // clear the filters now. - for (auto& i: m_filters) - i.second.changes.clear(); -} - void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash) { Guard l(x_filtersWatches); @@ -342,22 +320,6 @@ void Client::setForceMining(bool _enable) m.noteStateChange(); } -void Client::setMiningThreads(unsigned _threads) -{ - stopMining(); - auto t = _threads ? _threads : thread::hardware_concurrency(); -#if ETH_ETHASHCL || !ETH_TRUE - if (m_turboMining) - t = 1; -#endif - WriteGuard l(x_localMiners); - m_localMiners.clear(); - m_localMiners.resize(t); - unsigned i = 0; - for (auto& m: m_localMiners) - m.setup(this, i++); -} - MineProgress Client::miningProgress() const { MineProgress ret; @@ -452,160 +414,161 @@ pair Client::getWork() return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); } -bool Client::submitWork(ProofOfWork::Solution const& _proof) +bool Client::submitWork(ProofOfWork::Solution const& _solution) { - Guard l(x_remoteMiner); - return m_remoteMiner.submitWork(_proof); + bytes newBlock; + { + WriteGuard l(x_stateDB); + if (!m_postMine.completeMine(_solution)) + return false; + newBlock = m_postMine.blockData(); + } + + ImportRoute ir = m_bc.attemptImport(newBlock, m_stateDB); + if (!ir.first.empty()) + onChainChanged(ir); + return true; } -void Client::doWork() +void Client::syncBlockQueue() { - // TODO: Use condition variable rather than polling. - - bool stillGotWork = false; - - cworkin << "WORK"; - h256Set changeds; - - auto maintainMiner = [&](OldMiner& m) - { - if (m.isComplete()) - { - // TODO: enable a short-circuit option since we mined it. will need to get the end state from the miner. - auto lm = dynamic_cast(&m); - h256s hs; - h256 c; - if (false && lm && !m_verifyOwnBlocks) - { - // TODO: implement - //m_bc.attemptImport(m_blockData(), m_stateDB, lm->state()); - // TODO: derive hs from lm->state() - } - else - { - cwork << "CHAIN <== postSTATE"; - WriteGuard l(x_stateDB); - tie(hs, c) = m_bc.attemptImport(m.blockData(), m_stateDB); - } - if (hs.size()) - { - for (auto const& h: hs) - if (h != c) - appendFromNewBlock(h, changeds); - changeds.insert(ChainChangedFilter); - } - for (auto& m: m_localMiners) - m.noteStateChange(); - } - }; - { - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - maintainMiner(m); - } - { - Guard l(x_remoteMiner); - maintainMiner(m_remoteMiner); - } + ImportResult ir; - // Synchronise state to block chain. - // This should remove any transactions on our queue that are included within our state. - // It also guarantees that the state reflects the longest (valid!) chain on the block chain. - // This might mean reverting to an earlier state and replaying some blocks, or, (worst-case: - // if there are no checkpoints before our fork) reverting to the genesis block and replaying - // all blocks. - // Resynchronise state with block chain & trans - bool resyncStateNeeded = false; { WriteGuard l(x_stateDB); cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; x_stateDB.unlock(); - h256s fresh; - h256s dead; - bool sgw; - tie(fresh, dead, sgw) = m_bc.sync(m_bq, db, 100); - - // insert transactions that we are declaring the dead part of the chain - for (auto const& h: dead) - { - clog(ClientNote) << "Dead block:" << h.abridged(); - for (auto const& t: m_bc.transactions(h)) - { - clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); - m_tq.import(t); - } - } - // remove transactions from m_tq nicely rather than relying on out of date nonce later on. - for (auto const& h: fresh) - { - clog(ClientChat) << "Live block:" << h.abridged(); - for (auto const& th: m_bc.transactionHashes(h)) - { - clog(ClientNote) << "Safely dropping transaction " << th.abridged(); - m_tq.drop(th); - } - } + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); - stillGotWork = stillGotWork | sgw; - if (!fresh.empty()) - { - for (auto i: fresh) - appendFromNewBlock(i, changeds); - changeds.insert(ChainChangedFilter); - } x_stateDB.lock(); if (fresh.size()) m_stateDB = db; + } + + if (!ir.first.empty()) + onChainChanged(ir); + return true; +} + +void Client::syncTransactionQueue() +{ + // returns TransactionReceipts, once for each transaction. + cwork << "postSTATE <== TQ"; + TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + if (newPendingReceipts.size()) + { + for (size_t i = 0; i < newPendingReceipts.size(); i++) + appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); - cwork << "preSTATE <== CHAIN"; - if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) + changeds.insert(PendingChangedFilter); + + if (isMining()) + cnote << "Additional transaction ready: Restarting mining operation."; + resyncStateNeeded = true; + if (auto h = m_host.lock()) + h->noteNewTransactions(); + } +} + +void Client::onChainChanged(ImportRoute const& _ir) +{ + // insert transactions that we are declaring the dead part of the chain + for (auto const& h: _ir.second) + { + clog(ClientNote) << "Dead block:" << h.abridged(); + for (auto const& t: m_bc.transactions(h)) { - if (isMining()) - cnote << "New block on chain: Restarting mining operation."; - m_postMine = m_preMine; - resyncStateNeeded = true; - changeds.insert(PendingChangedFilter); - // TODO: Move transactions pending from m_postMine back to transaction queue. + clog(ClientNote) << "Resubmitting transaction " << Transaction(t, CheckTransaction::None); + m_tq.import(t); } + } - // returns TransactionReceipts, once for each transaction. - cwork << "postSTATE <== TQ"; - TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); - if (newPendingReceipts.size()) + // remove transactions from m_tq nicely rather than relying on out of date nonce later on. + for (auto const& h: _ir.first) + { + clog(ClientChat) << "Live block:" << h.abridged(); + for (auto const& th: m_bc.transactionHashes(h)) { - for (size_t i = 0; i < newPendingReceipts.size(); i++) - appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); - - changeds.insert(PendingChangedFilter); - - if (isMining()) - cnote << "Additional transaction ready: Restarting mining operation."; - resyncStateNeeded = true; - if (auto h = m_host.lock()) - h->noteNewTransactions(); + clog(ClientNote) << "Safely dropping transaction " << th.abridged(); + m_tq.drop(th); } } - if (!changeds.empty()) - if (auto h = m_host.lock()) - h->noteNewBlocks(); + if (auto h = m_host.lock()) + h->noteNewBlocks(); + + h256Set changeds; + for (auto const& h: _ir.first) + if (h != _ir.second) + appendFromNewBlock(h, changeds); + changeds.insert(ChainChangedFilter); + noteChanged(changeds); + + // RESTART MINING - if (resyncStateNeeded) + // LOCKS NEEDED? + Guard l(x_stateDB); + cwork << "preSTATE <== CHAIN"; + if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - m.noteStateChange(); + if (isMining()) + cnote << "New block on chain: Restarting mining operation."; + m_postMine = m_preMine; + resyncStateNeeded = true; + changeds.insert(PendingChangedFilter); + + m_postMine.commitToMine(m_bc); + m_farm.setWork(m_postMine.info()); } +} - cwork << "noteChanged" << changeds.size() << "items"; - noteChanged(changeds); - cworkout << "WORK"; +void Client::noteChanged(h256Set const& _filters) +{ + Guard l(x_filtersWatches); + if (_filters.size()) + cnote << "noteChanged(" << filtersToString(_filters) << ")"; + // accrue all changes left in each filter into the watches. + for (auto& w: m_watches) + if (_filters.count(w.second.id)) + { + cwatch << "!!!" << w.first << (m_filters.count(w.second.id) ? w.second.id.abridged() : w.second.id == PendingChangedFilter ? "pending" : w.second.id == ChainChangedFilter ? "chain" : "???"); + if (m_filters.count(w.second.id)) // Normal filtering watch + w.second.changes += m_filters.at(w.second.id).changes; + else // Special ('pending'/'latest') watch + w.second.changes.push_back(LocalisedLogEntry(SpecialLogEntry, 0)); + } + // clear the filters now. + for (auto& i: m_filters) + i.second.changes.clear(); +} + +void Client::doWork() +{ + // TODO: Use condition variable rather than this rubbish. + + Guard l(x_fakeSignalSystemState); + + if (m_syncTransactionQueue) + { + m_syncTransactionQueue = false; + syncTransactionQueue(); + } + + if (m_syncBlockQueue) + { + m_syncBlockQueue = false; + syncBlockQueue(); + } + + checkWatchGarbage(); - if (!stillGotWork) - this_thread::sleep_for(chrono::milliseconds(100)); + this_thread::sleep_for(chrono::milliseconds(20)); +} +void Client::checkWatchGarbage() +{ if (chrono::system_clock::now() - m_lastGarbageCollection > chrono::seconds(5)) { // watches garbage collection diff --git a/libethereum/Client.h b/libethereum/Client.h index 9af501f74..8eac2e577 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -42,6 +42,7 @@ #include "CommonNet.h" #include "Miner.h" #include "ABI.h" +#include "Farm.h" #include "ClientBase.h" namespace dev @@ -122,7 +123,7 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C /** * @brief Main API hub for interfacing with Ethereum. */ -class Client: public MinerHost, public ClientBase, Worker +class Client: public ClientBase, Worker { friend class OldMiner; @@ -132,8 +133,7 @@ public: p2p::Host* _host, std::string const& _dbPath = std::string(), WithExisting _forceAction = WithExisting::Trust, - u256 _networkId = 0, - int _miners = -1 + u256 _networkId = 0 ); explicit Client( @@ -141,8 +141,7 @@ public: std::shared_ptr _gpForAdoption, // pass it in with new. std::string const& _dbPath = std::string(), WithExisting _forceAction = WithExisting::Trust, - u256 _networkId = 0, - int _miners = -1 + u256 _networkId = 0 ); /// Destructor. @@ -191,31 +190,31 @@ public: /// Are we allowed to GPU mine? bool turboMining() const { return m_turboMining; } /// Enable/disable GPU mining. - void setTurboMining(bool _enable = true) { bool was = isMining(); stopMining(); m_turboMining = _enable; setMiningThreads(0); if (was) startMining(); } + void setTurboMining(bool _enable = true) { m_turboMining = _enable; if (isMining()) startMining(); } - /// Stops mining and sets the number of mining threads (0 for automatic). - void setMiningThreads(unsigned _threads = 0) override; - /// Get the effective number of mining threads. - unsigned miningThreads() const override { ReadGuard l(x_localMiners); return m_localMiners.size(); } /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { startWorking(); { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.start(); } } + void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); } /// Stop mining. /// NOT thread-safe - void stopMining() override { { ReadGuard l(x_localMiners); for (auto& m: m_localMiners) m.stop(); } } - /// Are we mining now? - bool isMining() const override { { ReadGuard l(x_localMiners); if (!m_localMiners.empty() && m_localMiners[0].isRunning()) return true; } return false; } + void stopMining() override { m_farm.stop(); } /// Are we mining now? + bool isMining() const override { return m_farm.isMining(); } + /// The hashrate... uint64_t hashrate() const override; /// Check the progress of the mining. - MineProgress miningProgress() const override; + MiningProgress miningProgress() const override; /// Get and clear the mining history. std::list miningHistory(); /// Update to the latest transactions and get hash of the current block to be mined minus the /// nonce (the 'work hash') and the difficulty to be met. virtual std::pair getWork() override; - /// Submit the proof for the proof-of-work. + + /** @brief Submit the proof for the proof-of-work. + * @param _s A valid solution. + * @return true if the solution was indeed valid and accepted. + */ virtual bool submitWork(ProofOfWork::Solution const& _proof) override; // Debug stuff: @@ -261,10 +260,23 @@ private: /// Called when Worker is exiting. virtual void doneWorking(); - /// Overrides for being a mining host. - virtual void setupState(State& _s); - virtual bool turbo() const { return m_turboMining; } - virtual bool force() const { return m_forceMining; } + /// Magically called when the chain has changed. An import route is provided. + /// Called by either submitWork() or in our main thread through syncBlockQueue(). + void onChainChanged(ImportRoute const& _ir); + + /// Signal handler for when the block queue needs processing. + void syncBlockQueue(); + + /// Signal handler for when the block queue needs processing. + void syncTransactionQueue(); + + /// Magically called when m_tq needs syncing. Be nice and don't block. + void onTransactionQueueReady() { Guard l(x_fakeSignalSystemState); m_syncTransactionQueue = true; } + + /// Magically called when m_tq needs syncing. Be nice and don't block. + void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; } + + void checkWatchGarbage(); VersionChecker m_vc; ///< Dummy object to check & update the protocol version. CanonBlockChain m_bc; ///< Maintains block database. @@ -278,17 +290,24 @@ private: std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. + Farm m_farm; ///< Our mining farm. mutable Mutex x_remoteMiner; ///< The remote miner lock. RemoteMiner m_remoteMiner; ///< The remote miner. - std::vector m_localMiners; ///< The in-process miners. - mutable SharedMutex x_localMiners; ///< The in-process miners lock. - bool m_paranoia = false; ///< Should we be paranoid about our state? + Handler m_tqReady; + Handler m_bqReady; + 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? - bool m_verifyOwnBlocks = true; ///< Should be verify blocks that we mined? + bool m_paranoia = false; ///< Should we be paranoid about our state? mutable std::chrono::system_clock::time_point m_lastGarbageCollection; + ///< When did we last both doing GC on the watches? + + // TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables) + mutable Mutex x_fakeSignalSystemState; + bool m_syncTransactionQueue = false; + bool m_syncBlockQueue = false; }; } diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index ae6d27578..4b3cc5002 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -142,8 +142,6 @@ public: /// TODO: consider moving it to a separate interface - virtual void setMiningThreads(unsigned _threads) override { (void)_threads; BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::setMiningThreads")); } - virtual unsigned miningThreads() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningThreads")); } virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); } virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); } diff --git a/libethereum/Farm.cpp b/libethereum/Farm.cpp index 639e4efcf..e69de29bb 100644 --- a/libethereum/Farm.cpp +++ b/libethereum/Farm.cpp @@ -1,12 +0,0 @@ -#include "Farm.h" - -Farm::Farm() -{ - -} - -Farm::~Farm() -{ - -} - diff --git a/libethereum/Farm.h b/libethereum/Farm.h index a49038f0d..55d1aa8df 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -14,10 +14,9 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file Miner.h - * @author Alex Leverington +/** @file Farm.h * @author Gav Wood - * @date 2014 + * @date 2015 */ #pragma once @@ -28,7 +27,8 @@ #include #include #include -#include "Miner.h" +#include +#include namespace dev { @@ -41,8 +41,8 @@ namespace eth * Miners ask for work, then submit proofs * @threadsafe */ -template -class Farm: public FarmFace +template +class Farm: public FarmFace { public: /** @@ -65,13 +65,13 @@ public: * @brief (Re)start miners for CPU only. * @returns true if started properly. */ - bool startCPU() { return start(); } + bool startCPU() { return start(); } /** * @brief (Re)start miners for GPU only. * @returns true if started properly. */ - bool startGPU() { start(); } + bool startGPU() { start(); } /** * @brief Stop all mining activities. @@ -82,20 +82,24 @@ public: m_miners.clear(); } + bool isMining() const + { + ReadGuard l(x_miners); + return !m_miners.empty(); + } + /** * @brief Get information on the progress of mining this work package. * @return The progress with mining so far. */ - MineProgress const& mineProgress() const { ReadGuard l(x_progress); return m_progress; } + MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; } -protected: - // TO BE REIMPLEMENTED BY THE SUBCLASS /** * @brief Provides a valid header based upon that received previously with setWork(). * @param _bi The now-valid header. * @return true if the header was good and that the Farm should pause until more work is submitted. */ - virtual bool submitHeader(BlockInfo const& _bi) = 0; + void onSolutionFound(function _handler) { m_onSolutionFound = _handler; } private: /** @@ -104,16 +108,15 @@ private: * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ - bool submitProof(ProofOfWork::Solution const& _p, WorkPackage const& _wp, NewMiner* _m) override + bool submitProof(Solution const& _s, WorkPackage const& _wp, Miner* _m) override { if (_wp.headerHash != m_work.headerHash) return false; - ProofOfWork::assignResult(_p, m_header); - if (submitHeader(m_header)) + if (m_onSolutionFound && m_onSolutionFound(_s)) { ReadGuard l(x_miners); - for (std::shared_ptr const& m: m_miners) + for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->pause(); m_work.headerHash = h256(); @@ -139,14 +142,16 @@ private: } mutable SharedMutex x_miners; - std::vector> m_miners; + std::vector> m_miners; mutable SharedMutex x_progress; - MineProgress m_progress; + MiningProgress m_progress; mutable SharedMutex x_work; WorkPackage m_work; BlockInfo m_header; + + function m_onSolutionFound; }; } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index e77565837..75618eb5f 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -856,20 +856,6 @@ void State::commitToMine(BlockChain const& _bc) m_committedToMine = true; } -bool State::completeMine(ProofOfWork::Solution const& _nonce) -{ - ProofOfWork::assignResult(_nonce, m_currentBlock); - -// if (!m_pow.verify(m_currentBlock)) -// return false; - - cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock); - - completeMine(); - - return true; -} - void State::completeMine() { cdebug << "Completing mine!"; diff --git a/libethereum/State.h b/libethereum/State.h index b327378a1..36a505481 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "TransactionQueue.h" @@ -162,30 +163,19 @@ public: /// Pass in a solution to the proof-of-work. /// @returns true iff the given nonce is a proof-of-work for this State's block. - bool completeMine(ProofOfWork::Solution const& _result); - - /// Attempt to find valid nonce for block that this state represents. - /// This function is thread-safe. You can safely have other interactions with this object while it is happening. - /// @param _msTimeout Timeout before return in milliseconds. - /// @returns Information on the mining. - template MineInfo mine(ProofOfWork* _pow) + template + bool completeMine(typename PoW::Solution const& _result) { - // Update difficulty according to timestamp. - m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); + PoW::assignResult(_result, m_currentBlock); - MineInfo ret; - typename ProofOfWork::Solution r; - std::tie(ret, r) = _pow->mine(m_currentBlock, _pow->defaultTimeout(), true); + // if (!m_pow.verify(m_currentBlock)) + // return false; - if (!ret.completed) - m_currentBytes.clear(); - else - { - ProofOfWork::assignResult(r, m_currentBlock); - cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << ProofOfWork::verify(m_currentBlock); - } + cnote << "Completed" << m_currentBlock.headerHash(WithoutNonce).abridged() << m_currentBlock.nonce.abridged() << m_currentBlock.difficulty << PoW::verify(m_currentBlock); - return ret; + completeMine(); + + return true; } /** Commit to DB and build the final block if the previous call to mine()'s result is completion. @@ -369,7 +359,6 @@ private: /// Debugging only. Good for checking the Trie is in shape. void paranoia(std::string const& _when, bool _enforceRefs = false) const; - OverlayDB m_db; ///< Our overlay for the state tree. SecureTrieDB m_state; ///< Our state tree, as an OverlayDB DB. Transactions m_transactions; ///< The current list of transactions that we've included in the state. diff --git a/libethereum/TransactionQueue.cpp b/libethereum/TransactionQueue.cpp index 7c72f53e8..506de2d2f 100644 --- a/libethereum/TransactionQueue.cpp +++ b/libethereum/TransactionQueue.cpp @@ -53,6 +53,7 @@ ImportResult TransactionQueue::import(bytesConstRef _transactionRLP, ImportCallb if (_cb) m_callbacks[h] = _cb; ctxq << "Queued vaguely legit-looking transaction" << h.abridged(); + m_onReady(); } catch (Exception const& _e) { diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index ad093b4e5..e18f47e80 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -26,7 +26,7 @@ #include #include #include -#include "libethcore/Common.h" +#include #include "Transaction.h" namespace dev @@ -60,13 +60,15 @@ public: void noteGood(std::pair const& _t); void clear() { WriteGuard l(m_lock); m_known.clear(); m_current.clear(); m_unknown.clear(); } + template Handler onReady(T const& _t) { return m_onReady.add(_t); } private: mutable boost::shared_mutex m_lock; ///< General lock. std::set m_known; ///< Hashes of transactions in both sets. std::map m_current; ///< Map of SHA3(tx) to tx. std::multimap> m_unknown; ///< 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_callbacks; ///< Called once + std::map> m_callbacks; ///< Called once. + Signal m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. }; } From 15f74352e3352faaa55477b6eeee83f36c4b0128 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 00:08:52 +0200 Subject: [PATCH 23/34] Compile fixes. --- libethcore/Common.h | 37 +++++++++++++------------------ libethcore/Ethash.cpp | 40 +++++++++++++++------------------- libethcore/Ethash.h | 12 ++++++---- libethcore/EthashAux.cpp | 36 +++++++++++++++--------------- libethcore/EthashAux.h | 6 +++-- libethcore/Miner.h | 16 ++++++++------ libethereum/Client.h | 2 +- libethereum/Farm.h | 2 +- libethereum/Interface.h | 5 ----- libethereum/TransactionQueue.h | 2 +- mix/MixClient.cpp | 10 --------- mix/MixClient.h | 2 -- 12 files changed, 75 insertions(+), 95 deletions(-) diff --git a/libethcore/Common.h b/libethcore/Common.h index b5291648b..4d01055f1 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -96,42 +96,35 @@ enum class ImportResult BadChain }; -class Signal; - -class Handler +/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. +class Signal { public: - Handler() = default; - Handler(Handler const&) = delete; - ~Handler() { reset(); } + class HandlerAux + { + friend class Signal; - Handler& operator=(Handler const& _h) = delete; + public: + ~HandlerAux() { if (m_s) m_s->m_fire.erase(m_i); m_s = nullptr; } - void reset(); - -private: - Handler(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} + private: + HandlerAux(unsigned _i, Signal* _s): m_i(_i), m_s(_s) {} - unsigned m_i = 0; - Signal* m_s = nullptr; -}; + unsigned m_i = 0; + Signal* m_s = nullptr; + }; -/// Super-duper signal mechanism. TODO: replace with somthing a bit heavier weight. -class Signal -{ -public: using Callback = std::function; - using Callbacks = std::vector; - Handler add(Callback const& _h) { auto n = m_onReady.size() ? m_onReady.rbegin()->first + 1 : 0; m_onReady[n] = _h; return Handler(n, this); } + std::shared_ptr add(Callback const& _h) { auto n = m_fire.empty() ? 0 : (m_fire.rbegin()->first + 1); m_fire[n] = _h; return std::shared_ptr(new HandlerAux(n, this)); } void operator()() { for (auto const& f: m_fire) f.second(); } private: - std::map m_fire; + std::map m_fire; }; -inline void Handler::reset() { if (m_s) m_s->m_fire->erase(m_i); m_s = nullptr; } +using Handler = std::shared_ptr; } } diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 82e349b4c..f3b4d1b18 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -62,7 +62,7 @@ unsigned Ethash::revision() void Ethash::prep(BlockInfo const& _header) { if (_header.number % ETHASH_EPOCH_LENGTH == 1) - EthashAux::full(bi); + EthashAux::full(_header); } bool Ethash::preVerify(BlockInfo const& _header) @@ -88,7 +88,7 @@ bool Ethash::verify(BlockInfo const& _header) #endif h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - auto result = eval(_header); + auto result = EthashAux::eval(_header); bool slow = result.value <= boundary && result.mixHash == _header.mixHash; #if ETH_DEBUG || !ETH_TRUE @@ -111,29 +111,27 @@ bool Ethash::verify(BlockInfo const& _header) void Ethash::CPUMiner::workLoop() { auto tid = std::this_thread::get_id(); - static std::mt19937_64 s_eng((time(0) + *reinterpret_cast(m_last.data()) + std::hash()(tid))); + static std::mt19937_64 s_eng((time(0) + std::hash()(tid))); - uint64_t tryNonce = Nonce::random(s_eng); + uint64_t tryNonce = (uint64_t)(u64)Nonce::random(s_eng); ethash_return_value ethashReturn; - auto p = Ethash::params(m_work.seedHash); - void const* dagPointer = Ethash::full(m_work.headerHash).data(); + auto p = EthashAux::params(m_work.seedHash); + void const* dagPointer = EthashAux::full(m_work.headerHash).data(); uint8_t const* headerHashPointer = m_work.headerHash.data(); - h256 boundary = m_work.boundary(); + h256 boundary = m_work.boundary; unsigned hashCount = 0; for (; !shouldStop(); tryNonce++, hashCount++) { ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); - if (value <= boundary && submitProof(Solution{value, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) + if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) break; } } #if ETH_ETHASHCL || !ETH_TRUE -namespace dev { namespace eth { - class EthashCLHook: public ethash_cl_miner::search_hook { public: @@ -148,8 +146,8 @@ public: m_abort = true; for (unsigned timeout = 0; timeout < 100 && !m_aborted; ++timeout) std::this_thread::sleep_for(chrono::milliseconds(30)); - if (!m_aborted) - cwarn << "Couldn't abort. Abandoning OpenCL process."; +// if (!m_aborted) +// cwarn << "Couldn't abort. Abandoning OpenCL process."; m_aborted = m_abort = false; } @@ -161,7 +159,7 @@ protected: // cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); for (uint32_t i = 0; i < _count; ++i) { - if (m_owner->found(_nonces[i])) + if (m_owner->report(_nonces[i])) { m_aborted = true; return true; @@ -193,19 +191,17 @@ private: Ethash::GPUMiner* m_owner = nullptr; }; -} } - Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Miner(_ci), m_hook(new EthashCLHook(this)) { } -void Ethash::GPUMiner::report(uint64_t _nonce) +bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; - Result r = Ethash::eval(m_work.seedHash, m_work.headerHash, n); - if (r.value < m_work.boundary) + Result r = EthashAux::eval(m_lastWork.seedHash, m_lastWork.headerHash, n); + if (r.value < m_lastWork.boundary) return submitProof(Solution{n, r.mixHash}); return false; } @@ -217,17 +213,17 @@ void Ethash::GPUMiner::kickOff(WorkPackage const& _work) if (m_miner) m_hook->abort(); m_miner.reset(new ethash_cl_miner); - auto p = Ethash::params(_work.seedHash); - auto cb = [&](void* d) { EthashAux::readFull(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + auto p = EthashAux::params(_work.seedHash); + auto cb = [&](void* d) { EthashAux::full(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); } if (m_lastWork.headerHash != _work.headerHash) { m_hook->abort(); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); - m_miner->search(_work.headerHash, upper64OfBoundary, *m_hook); + m_miner->search(_work.headerHash.data(), upper64OfBoundary, *m_hook); } - m_work = _work; + m_lastWork = _work; } void Ethash::GPUMiner::pause() diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index bdd6bf6c5..b87f06549 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "Common.h" #include "BlockInfo.h" #include "Miner.h" @@ -37,9 +38,13 @@ namespace dev namespace eth { +class EthashCLHook; + class Ethash { public: + using Miner = GenericMiner; + struct Solution { Nonce nonce; @@ -73,7 +78,7 @@ public: public: CPUMiner(ConstructionInfo const& _ci): Miner(_ci), Worker("miner" + toString(index())) {} - static unsigned instances() { return thread::hardware_concurrency(); } + static unsigned instances() { return std::thread::hardware_concurrency(); } protected: void kickOff(WorkPackage const& _work) override @@ -93,10 +98,9 @@ public: }; #if ETH_ETHASHCL || !ETH_TRUE - class EthashCLHook; class GPUMiner: public Miner { - friend class EthashCLHook; + friend class dev::eth::EthashCLHook; public: GPUMiner(ConstructionInfo const& _ci); @@ -108,7 +112,7 @@ public: void pause() override; private: - void report(uint64_t _nonce); + bool report(uint64_t _nonce); std::unique_ptr m_hook; std::unique_ptr m_miner; diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index a6143e264..969310dac 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -102,13 +102,13 @@ void const* EthashAux::light(BlockInfo const& _header) void const* EthashAux::light(h256 const& _seedHash) { - RecursiveGuard l(x_this); - if (!m_lights.count(_header.seedHash())) + RecursiveGuard l(get()->x_this); + if (!get()->m_lights.count(_seedHash)) { ethash_params p = params(_seedHash); - m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); + get()->m_lights[_seedHash] = ethash_new_light(&p, _seedHash.data()); } - return m_lights[_seedHash]; + return get()->m_lights[_seedHash]; } bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest) @@ -118,14 +118,14 @@ bytesConstRef EthashAux::full(BlockInfo const& _header, bytesRef _dest) bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) { - RecursiveGuard l(x_this); - if (m_fulls.count(_seedHash) && _dest) + RecursiveGuard l(get()->x_this); + if (get()->m_fulls.count(_seedHash) && _dest) { - assert(m_fulls.size() <= _dest.size()); - m_fulls.at(_seedHash).copyTo(_dest); - return; + assert(get()->m_fulls.size() <= _dest.size()); + get()->m_fulls.at(_seedHash).copyTo(_dest); + return _dest; } - if (!m_fulls.count(_seedHash)) + if (!get()->m_fulls.count(_seedHash)) { // @memoryleak @bug place it on a pile for deletion - perhaps use shared_ptr. /* if (!m_fulls.empty()) @@ -138,7 +138,7 @@ bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) boost::filesystem::create_directories(getDataDir("ethash")); } catch (...) {} - auto info = rlpList(revision(), _seedHash); + auto info = rlpList(Ethash::revision(), _seedHash); std::string oldMemoFile = getDataDir("ethash") + "/full"; std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8)); if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) @@ -147,8 +147,8 @@ bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) boost::filesystem::rename(oldMemoFile, memoFile); } - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); - IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); + ETH_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); + ETH_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); ethash_params p = params(_seedHash); assert(!_dest || _dest.size() >= p.full_size); // must be big enough. @@ -162,14 +162,14 @@ bytesConstRef EthashAux::full(h256 const& _seedHash, bytesRef _dest) r = _dest; else r = bytesRef(new byte[p.full_size], p.full_size); - ethash_prep_full(r, &p, light(_seedHash)); + ethash_prep_full(r.data(), &p, light(_seedHash)); writeFile(memoFile, r); } if (_dest) return _dest; - m_fulls[_seedHash] = r; + get()->m_fulls[_seedHash] = r; } - return m_fulls[_seedHash]; + return get()->m_fulls[_seedHash]; } Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) @@ -179,12 +179,12 @@ Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) { - auto p = EthashAux::params(_header); + auto p = EthashAux::params(_seedHash); ethash_return_value r; if (EthashAux::get()->m_fulls.count(_seedHash)) ethash_compute_full(&r, EthashAux::get()->full(_seedHash).data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); else ethash_compute_light(&r, EthashAux::get()->light(_seedHash), &p, _headerHash.data(), (uint64_t)(u64)_nonce); // cdebug << "EthashAux::eval sha3(cache):" << sha3(EthashAux::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); - return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; + return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index bfd01a594..aec1089a2 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -28,7 +28,7 @@ namespace eth{ class EthashAux { - EthashAux() {} +public: ~EthashAux(); static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; } @@ -49,9 +49,11 @@ class EthashAux static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); private: + EthashAux() {} + void killCache(h256 const& _s); - static Ethash* s_this; + static EthashAux* s_this; RecursiveMutex x_this; std::map m_lights; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index e7157e660..9372c06b1 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -58,19 +58,19 @@ struct MiningProgress unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; -template class Miner; +template class GenericMiner; /** * @brief Class for hosting one or more Miners. * @warning Must be implemented in a threadsafe manner since it will be called from multiple * miner threads. */ -template class FarmFace +template class GenericFarmFace { public: using WorkPackage = typename PoW::WorkPackage; using Solution = typename PoW::Solution; - using Miner = Miner; + using Miner = GenericMiner; /** * @brief Called from a Miner to note a WorkPackage has a solution. @@ -85,15 +85,15 @@ public: /** * @brief A miner - a member and adoptee of the Farm. */ -template class Miner +template class GenericMiner { public: - using ConstructionInfo = std::pair*, unsigned>; using WorkPackage = typename PoW::WorkPackage; using Solution = typename PoW::Solution; - using FarmFace = FarmFace; + using FarmFace = GenericFarmFace; + using ConstructionInfo = std::pair; - Miner(ConstructionInfo const& _ci): + GenericMiner(ConstructionInfo const& _ci): m_farm(_ci.first), m_index(_ci.second) {} @@ -144,6 +144,8 @@ protected: return true; } + WorkPackage const& work() const { return m_work; } + private: FarmFace* m_farm = nullptr; unsigned m_index; diff --git a/libethereum/Client.h b/libethereum/Client.h index 8eac2e577..a136033e5 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -290,7 +290,7 @@ private: std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. - Farm m_farm; ///< Our mining farm. + GenericFarm m_farm; ///< Our mining farm. mutable Mutex x_remoteMiner; ///< The remote miner lock. RemoteMiner m_remoteMiner; ///< The remote miner. diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 55d1aa8df..39988c8d7 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -42,7 +42,7 @@ namespace eth * @threadsafe */ template -class Farm: public FarmFace +class GenericFarm: public GenericFarmFace { public: /** diff --git a/libethereum/Interface.h b/libethereum/Interface.h index cf2e7f5ea..134bed53b 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -171,11 +171,6 @@ public: /// Get the coinbase address. virtual Address address() const = 0; - /// Stops mining and sets the number of mining threads (0 for automatic). - virtual void setMiningThreads(unsigned _threads = 0) = 0; - /// Get the effective number of mining threads. - virtual unsigned miningThreads() const = 0; - /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread virtual void startMining() = 0; diff --git a/libethereum/TransactionQueue.h b/libethereum/TransactionQueue.h index e18f47e80..c3df00d25 100644 --- a/libethereum/TransactionQueue.h +++ b/libethereum/TransactionQueue.h @@ -48,7 +48,7 @@ class TransactionQueue public: using ImportCallback = std::function; - ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx); } + ImportResult import(bytes const& _tx, ImportCallback const& _cb = ImportCallback()) { return import(&_tx, _cb); } ImportResult import(bytesConstRef _tx, ImportCallback const& _cb = ImportCallback()); void drop(h256 _txHash); diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 5c905ef0b..2e8bd5384 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -372,16 +372,6 @@ void MixClient::setAddress(Address _us) m_state.setAddress(_us); } -void MixClient::setMiningThreads(unsigned _threads) -{ - m_miningThreads = _threads; -} - -unsigned MixClient::miningThreads() const -{ - return m_miningThreads; -} - void MixClient::startMining() { //no-op diff --git a/mix/MixClient.h b/mix/MixClient.h index 357e22930..649c7694f 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -63,8 +63,6 @@ public: dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber, bool _gasAuto, eth::FudgeFactor _ff = eth::FudgeFactor::Strict); void setAddress(Address _us) override; - void setMiningThreads(unsigned _threads) override; - unsigned miningThreads() const override; void startMining() override; void stopMining() override; bool isMining() const override; From e7008b58776a59834d6361467a541b404cb9a99a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 00:34:42 +0200 Subject: [PATCH 24/34] Various compile fixes. --- libethcore/Ethash.cpp | 9 ++ libethcore/Ethash.h | 6 +- libethereum/BlockChain.cpp | 3 +- libethereum/BlockChain.h | 2 +- libethereum/Client.cpp | 7 +- libethereum/Client.h | 24 +----- libethereum/Farm.h | 4 +- libethereum/Interface.h | 4 +- libethereum/Miner.cpp | 94 --------------------- libethereum/Miner.h | 166 ------------------------------------- 10 files changed, 22 insertions(+), 297 deletions(-) diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index f3b4d1b18..ac54ebe4d 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -59,6 +59,15 @@ unsigned Ethash::revision() return ETHASH_REVISION; } +Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) +{ + WorkPackage ret; + ret.boundary = _bi.boundary(); + ret.headerHash = _bi.headerHash(WithNonce); + ret.seedHash = _bi.seedHash(); + return ret; +} + void Ethash::prep(BlockInfo const& _header) { if (_header.number % ETHASH_EPOCH_LENGTH == 1) diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index b87f06549..458cb4e6c 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -68,10 +68,11 @@ public: static std::string name(); static unsigned revision(); + static void prep(BlockInfo const& _header); static bool verify(BlockInfo const& _header); static bool preVerify(BlockInfo const& _header); + static WorkPackage package(BlockInfo const& _header); static void assignResult(Solution const& _r, BlockInfo& _header) { _header.nonce = _r.nonce; _header.mixHash = _r.mixHash; } - static void prep(BlockInfo const& _header); class CPUMiner: public Miner, Worker { @@ -125,8 +126,5 @@ public: #endif }; -using ProofOfWork = Ethash; -using Solution = Ethash::Solution; - } } diff --git a/libethereum/BlockChain.cpp b/libethereum/BlockChain.cpp index 0ff0a3628..b73a79c49 100644 --- a/libethereum/BlockChain.cpp +++ b/libethereum/BlockChain.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include "GenesisInfo.h" #include "State.h" @@ -336,7 +335,7 @@ ImportRoute BlockChain::attemptImport(bytes const& _block, OverlayDB const& _sta catch (...) { cwarn << "Unexpected exception! Could not import block!" << boost::current_exception_diagnostic_information(); - return make_pair(h256s(), h256()); + return make_pair(h256s(), h256s()); } } diff --git a/libethereum/BlockChain.h b/libethereum/BlockChain.h index cdff566fb..8b4a3dce4 100644 --- a/libethereum/BlockChain.h +++ b/libethereum/BlockChain.h @@ -68,7 +68,7 @@ ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0); using BlocksHash = std::map; using TransactionHashes = h256s; using UncleHashes = h256s; -using ImportRoute = std::pair; +using ImportRoute = std::pair; enum { ExtraDetails = 0, diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 15a7c9bec..81023f9ef 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -404,14 +404,17 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 return ret; } -pair Client::getWork() +ProofOfWork::WorkPackage Client::getWork() { Guard l(x_remoteMiner); + BlockInfo bi; { ReadGuard l(x_stateDB); m_remoteMiner.update(m_postMine, m_bc); + m_postMine.commitToMine(m_bc); + bi = m_postMine.info(); } - return make_pair(m_remoteMiner.workHash(), m_remoteMiner.difficulty()); + return ProofOfWork::package(bi); } bool Client::submitWork(ProofOfWork::Solution const& _solution) diff --git a/libethereum/Client.h b/libethereum/Client.h index a136033e5..9f6bbfeb6 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -73,28 +73,6 @@ private: std::string m_path; }; -class RemoteMiner: public OldMiner -{ -public: - RemoteMiner() {} - - void update(State const& _provisional, BlockChain const& _bc) { m_state = _provisional; m_state.commitToMine(_bc); } - - h256 workHash() const { return m_state.info().headerHash(IncludeNonce::WithoutNonce); } - u256 const& difficulty() const { return m_state.info().difficulty; } - - bool submitWork(ProofOfWork::Solution const& _result) { return (m_isComplete = m_state.completeMine(_result)); } - - virtual bool isComplete() const override { return m_isComplete; } - virtual bytes const& blockData() const { return m_state.blockData(); } - - virtual void noteStateChange() override {} - -private: - bool m_isComplete = false; - State m_state; -}; - class BasicGasPricer: public GasPricer { public: @@ -209,7 +187,7 @@ public: /// Update to the latest transactions and get hash of the current block to be mined minus the /// nonce (the 'work hash') and the difficulty to be met. - virtual std::pair getWork() override; + virtual ProofOfWork::WorkPackage getWork() override; /** @brief Submit the proof for the proof-of-work. * @param _s A valid solution. diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 39988c8d7..137c137d3 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -53,9 +53,7 @@ public: { WriteGuard l(x_work); m_header = _bi; - m_work.boundary = _bi.boundary(); - m_work.headerHash = _bi.headerHash(WithNonce); - m_work.seedHash = _bi.seedHash(); + m_work = PoW::package(m_header); ReadGuard l(x_miners); for (auto const& m: miners) m->setWork(m_work); diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 134bed53b..0ab81728b 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -26,11 +26,11 @@ #include #include #include +#include #include "LogFilter.h" #include "Transaction.h" #include "AccountDiff.h" #include "BlockDetails.h" -#include "Miner.h" namespace dev { @@ -188,7 +188,7 @@ public: virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0; /// Check the progress of the mining. - virtual MineProgress miningProgress() const = 0; + virtual MiningProgress miningProgress() const = 0; protected: int m_default = PendingBlock; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp index b386fe868..e69de29bb 100644 --- a/libethereum/Miner.cpp +++ b/libethereum/Miner.cpp @@ -1,94 +0,0 @@ -/* - 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 Miner.cpp - * @author Gav Wood - * @author Giacomo Tazzari - * @date 2014 - */ - -#include "Miner.h" - -#include -#include "State.h" -using namespace std; -using namespace dev; -using namespace dev::eth; - -OldMiner::~OldMiner() {} - -LocalMiner::LocalMiner(MinerHost* _host, unsigned _id) -{ - setup(_host, _id); -} - -void LocalMiner::setup(MinerHost* _host, unsigned _id) -{ - AsyncMiner::setup(_host, _id); - setName("miner-" + toString(m_id)); - m_pow.reset(_host->turbo() ? new Ethash : (Ethash*)new EthashCPU); -} - -void LocalMiner::doWork() -{ - // Do some mining. - if (m_miningStatus != Waiting && m_miningStatus != Mined) - { - if (m_miningStatus == Preparing) - { - m_host->setupState(m_mineState); - if (m_host->force() || m_mineState.pending().size()) - m_miningStatus = Mining; - else - m_miningStatus = Waiting; - - { - Guard l(x_mineInfo); - m_mineProgress.best = (double)-1; - m_mineProgress.hashes = 0; - m_mineProgress.ms = 0; - } - } - - if (m_miningStatus == Mining) - { - // Mine for a while. - MineInfo mineInfo = m_mineState.mine(m_pow.get()); - - { - Guard l(x_mineInfo); - m_mineProgress.best = min(m_mineProgress.best, mineInfo.best); - m_mineProgress.current = mineInfo.best; - m_mineProgress.requirement = mineInfo.requirement; - m_mineProgress.ms += 100; - m_mineProgress.hashes += mineInfo.hashes; - m_mineHistory.push_back(mineInfo); - } - if (mineInfo.completed) - { - m_mineState.completeMine(); - m_host->onComplete(); - m_miningStatus = Mined; - } - else - m_host->onProgressed(); - } - } - else - { - this_thread::sleep_for(chrono::milliseconds(100)); - } -} diff --git a/libethereum/Miner.h b/libethereum/Miner.h index 3abf93770..e69de29bb 100644 --- a/libethereum/Miner.h +++ b/libethereum/Miner.h @@ -1,166 +0,0 @@ -/* - 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 Miner.h - * @author Alex Leverington - * @author Gav Wood - * @date 2014 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "State.h" - -namespace dev -{ - -namespace eth -{ - -/** - * @brief Class for hosting one or more Miners. - * @warning Must be implemented in a threadsafe manner since it will be called from multiple - * miner threads. - */ -class MinerHost -{ -public: - virtual void setupState(State& _s) = 0; ///< Reset the given State object to the one that should be being mined. - virtual void onProgressed() {} ///< Called once some progress has been made. - virtual void onComplete() {} ///< Called once a block is found. - virtual bool force() const = 0; ///< @returns true iff the Miner should mine regardless of the number of transactions. - virtual bool turbo() const = 0; ///< @returns true iff the Miner should use GPU if possible. -}; - -class OldMiner -{ -public: - virtual ~OldMiner(); - - virtual void noteStateChange() = 0; - virtual bool isComplete() const = 0; - virtual bytes const& blockData() const = 0; -}; - -class AsyncMiner: public OldMiner -{ -public: - /// Null constructor. - AsyncMiner(): m_host(nullptr) {} - - /// Constructor. - AsyncMiner(MinerHost* _host, unsigned _id = 0): m_host(_host), m_id(_id) {} - - /// Setup its basics. - void setup(MinerHost* _host, unsigned _id = 0) { m_host = _host; m_id = _id; } - - /// Start mining. - virtual void start() {} - - /// Stop mining. - virtual void stop() {} - - /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). - virtual bool isRunning() const { return false; } - -protected: - MinerHost* m_host = nullptr; ///< Our host. - unsigned m_id = 0; ///< Our unique id. -}; - -/** - * @brief Implements Miner. - * To begin mining, use start() & stop(). noteStateChange() can be used to reset the mining and set up the - * State object according to the host. Use isRunning() to determine if the miner has been start()ed. - * Use isComplete() to determine if the miner has finished mining. - * - * blockData() can be used to retrieve the complete block, ready for insertion into the BlockChain. - * - * Information on the mining can be queried through miningProgress() and miningHistory(). - * @threadsafe - * @todo Signal Miner to restart once with condition variables. - */ -class LocalMiner: public AsyncMiner, Worker -{ -public: - /// Null constructor. - LocalMiner() {} - - /// Constructor. - LocalMiner(MinerHost* _host, unsigned _id = 0); - - /// Move-constructor. - LocalMiner(LocalMiner&& _m): Worker((Worker&&)_m) { std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); } - - /// Move-assignment. - LocalMiner& operator=(LocalMiner&& _m) { Worker::operator=((Worker&&)_m); std::swap(m_host, _m.m_host); std::swap(m_pow, _m.m_pow); return *this; } - - /// Destructor. Stops miner. - ~LocalMiner() { stop(); } - - /// Setup its basics. - void setup(MinerHost* _host, unsigned _id = 0); - - /// Start mining. - void start() { startWorking(); } - - /// Stop mining. - void stop() { stopWorking(); } - - /// Call to notify Miner of a state change. - virtual void noteStateChange() override { m_miningStatus = Preparing; } - - /// @returns true iff the mining has been start()ed. It may still not be actually mining, depending on the host's turbo() & force(). - bool isRunning() const override { return isWorking(); } - - /// @returns true if mining is complete. - virtual bool isComplete() const override { return m_miningStatus == Mined; } - - /// @returns the internal State object. - virtual bytes const& blockData() const override { return m_mineState.blockData(); } - - /// Check the progress of the mining. - MineProgress miningProgress() const { Guard l(x_mineInfo); return m_mineProgress; } - - /// Get and clear the mining history. - std::list miningHistory() { Guard l(x_mineInfo); auto ret = m_mineHistory; m_mineHistory.clear(); return ret; } - - /// @returns the state on which we mined. - State const& state() const { return m_mineState; } - -private: - /// Do some work on the mining. - virtual void doWork(); - - enum MiningStatus { Waiting, Preparing, Mining, Mined, Stopping, Stopped }; - MiningStatus m_miningStatus = Waiting; ///< TODO: consider mutex/atomic variable. - State m_mineState; ///< The state on which we are mining, generally equivalent to m_postMine. - std::unique_ptr m_pow; ///< Our miner. - - mutable Mutex x_mineInfo; ///< Lock for the mining progress & history. - MineProgress m_mineProgress; ///< What's our progress? - std::list m_mineHistory; ///< What the history of our mining? -}; - -} -} From f22540f0e05f4984d2f590e9a3a63c164b89ab5f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 01:38:04 +0200 Subject: [PATCH 25/34] Lots of build fixes. Now minimal version builds ok. --- alethzero/MainWin.cpp | 2 +- eth/main.cpp | 11 +- libethash-cl/ethash_cl_miner.cpp | 2 + libethash-cl/ethash_cl_miner.h | 2 + libethcore/Common.cpp | 3 +- libethcore/Common.h | 3 - libethcore/Ethash.cpp | 14 ++- libethcore/Ethash.h | 6 +- libethereum/Client.cpp | 124 ++++++++++------------ libethereum/Client.h | 10 +- libethereum/ClientBase.cpp | 4 +- libethereum/ClientBase.h | 43 ++++---- libethereum/Farm.h | 33 ++++-- libethereum/Interface.h | 2 +- libethereum/Miner.cpp | 0 libethereum/Miner.h | 0 libweb3jsonrpc/WebThreeStubServerBase.cpp | 5 +- libwebthree/WebThree.cpp | 4 +- libwebthree/WebThree.h | 3 +- 19 files changed, 143 insertions(+), 128 deletions(-) delete mode 100644 libethereum/Miner.cpp delete mode 100644 libethereum/Miner.h diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 374829a61..03d216a17 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -164,7 +164,7 @@ Main::Main(QWidget *parent) : statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->blockCount); - ui->blockCount->setText(QString("PV%2 D%3 H%4 v%5").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(c_ethashVersion).arg(dev::Version)); + ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(ProofOfWork::name()).arg(ProofOfWork::revision()).arg(dev::Version)); connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); diff --git a/eth/main.cpp b/eth/main.cpp index 08f4b1de4..66abefd54 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #if ETH_READLINE #include #include @@ -44,7 +45,6 @@ #include #include #endif -#include #include "BuildInfo.h" using namespace std; using namespace dev; @@ -208,7 +208,7 @@ void doInitDAG(unsigned _n) BlockInfo bi; bi.number = _n; cout << "Initializing DAG for epoch beginning #" << (bi.number / 30000 * 30000) << " (seedhash " << bi.seedHash().abridged() << "). This will take a while." << endl; - Ethasher::get()->full(bi); + Ethash::prep(bi); exit(0); } @@ -269,7 +269,6 @@ int main(int argc, char** argv) /// Mining params unsigned mining = ~(unsigned)0; - int miners = -1; bool forceMining = false; KeyPair sigKey = KeyPair::create(); Secret sessionSecret; @@ -478,8 +477,6 @@ int main(int argc, char** argv) g_logVerbosity = atoi(argv[++i]); else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) peers = atoi(argv[++i]); - else if ((arg == "-t" || arg == "--miners") && i + 1 < argc) - miners = atoi(argv[++i]); else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) { string m = argv[++i]; @@ -541,9 +538,7 @@ int main(int argc, char** argv) killChain, nodeMode == NodeMode::Full ? set{"eth", "shh"} : set(), netPrefs, - &nodesState, - miners - ); + &nodesState); if (mode == OperationMode::DAGInit) doInitDAG(web3.ethereum()->blockChain().number() + (initDAG == PendingBlock ? 30000 : 0)); diff --git a/libethash-cl/ethash_cl_miner.cpp b/libethash-cl/ethash_cl_miner.cpp index 96f1fa582..016d8af58 100644 --- a/libethash-cl/ethash_cl_miner.cpp +++ b/libethash-cl/ethash_cl_miner.cpp @@ -50,6 +50,8 @@ static void add_definition(std::string& source, char const* id, unsigned value) source.insert(source.begin(), buf, buf + strlen(buf)); } +ethash_cl_miner::search_hook::~search_hook() {} + ethash_cl_miner::ethash_cl_miner() : m_opencl_1_1() { diff --git a/libethash-cl/ethash_cl_miner.h b/libethash-cl/ethash_cl_miner.h index e478c739f..d3d9f0223 100644 --- a/libethash-cl/ethash_cl_miner.h +++ b/libethash-cl/ethash_cl_miner.h @@ -12,6 +12,8 @@ class ethash_cl_miner public: struct search_hook { + virtual ~search_hook(); // always a virtual destructor for a class with virtuals. + // reports progress, return true to abort virtual bool found(uint64_t const* nonces, uint32_t count) = 0; virtual bool searched(uint64_t start_nonce, uint32_t count) = 0; diff --git a/libethcore/Common.cpp b/libethcore/Common.cpp index f0e749aaa..a0ceb389e 100644 --- a/libethcore/Common.cpp +++ b/libethcore/Common.cpp @@ -23,6 +23,7 @@ #include #include #include "Exceptions.h" +#include "ProofOfWork.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -41,7 +42,7 @@ const unsigned c_databaseVersionModifier = 1; const unsigned c_databaseVersionModifier = 0; #endif -const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (c_ethashVersion << 9); +const unsigned c_databaseVersion = c_databaseBaseVersion + (c_databaseVersionModifier << 8) + (ProofOfWork::revision() << 9); vector> const& units() { diff --git a/libethcore/Common.h b/libethcore/Common.h index 4d01055f1..bb704405a 100644 --- a/libethcore/Common.h +++ b/libethcore/Common.h @@ -41,9 +41,6 @@ extern const unsigned c_minorProtocolVersion; /// Current database version. extern const unsigned c_databaseVersion; -/// Current database version. -extern const unsigned c_ethashVersion; - /// User-friendly string representation of the amount _b in wei. std::string formatBalance(bigint const& _b); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index ac54ebe4d..03c7a3654 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -70,8 +70,7 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) void Ethash::prep(BlockInfo const& _header) { - if (_header.number % ETHASH_EPOCH_LENGTH == 1) - EthashAux::full(_header); + EthashAux::full(_header); } bool Ethash::preVerify(BlockInfo const& _header) @@ -206,6 +205,12 @@ Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): { } +Ethash::GPUMiner::~GPUMiner() +{ + delete m_hook; + delete m_miner; +} + bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; @@ -221,7 +226,10 @@ void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { if (m_miner) m_hook->abort(); - m_miner.reset(new ethash_cl_miner); + + delete m_miner; + m_miner = new ethash_cl_miner; + auto p = EthashAux::params(_work.seedHash); auto cb = [&](void* d) { EthashAux::full(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 458cb4e6c..1a7d82149 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -105,6 +105,7 @@ public: public: GPUMiner(ConstructionInfo const& _ci); + ~GPUMiner(); static unsigned instances() { return 1; } @@ -115,8 +116,9 @@ public: private: bool report(uint64_t _nonce); - std::unique_ptr m_hook; - std::unique_ptr m_miner; + EthashCLHook* m_hook; + ethash_cl_miner* m_miner; + h256 m_minerSeed; WorkPackage m_lastWork; ///< Work loaded into m_miner. MineInfo m_info; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 81023f9ef..79216f609 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -126,9 +126,9 @@ Client::Client(p2p::Host* _extNet, std::string const& _dbPath, WithExisting _for m_preMine(m_stateDB, BaseState::CanonGenesis), m_postMine(m_stateDB) { - m_tqReady = m_tq->onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); - m_bqReady = m_bq->onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); - m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); m_gp->update(m_bc); @@ -151,9 +151,9 @@ Client::Client(p2p::Host* _extNet, std::shared_ptr _gp, std::string c m_preMine(m_stateDB), m_postMine(m_stateDB) { - m_tq->onReady([=](){ this->onTransactionQueueReady(); }); - m_bq->onReady([=](){ this->onBlockQueueReady(); }); - m_farm->onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); + m_tqReady = m_tq.onReady([=](){ this->onTransactionQueueReady(); }); // TODO: should read m_tq->onReady(thisThread, syncTransactionQueue); + m_bqReady = m_bq.onReady([=](){ this->onBlockQueueReady(); }); // TODO: should read m_bq->onReady(thisThread, syncBlockQueue); + m_farm.onSolutionFound([=](ProofOfWork::Solution const& s){ return this->submitWork(s); }); m_gp->update(m_bc); @@ -210,7 +210,7 @@ void Client::killChain() m_tq.clear(); m_bq.clear(); - m_localMiners.clear(); + m_farm.stop(); m_preMine = State(); m_postMine = State(); @@ -248,11 +248,7 @@ void Client::clearPending() m_postMine = m_preMine; } - { - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - m.noteStateChange(); - } + startMining(); noteChanged(changeds); } @@ -315,34 +311,23 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) void Client::setForceMining(bool _enable) { m_forceMining = _enable; - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - m.noteStateChange(); + startMining(); } -MineProgress Client::miningProgress() const +MiningProgress Client::miningProgress() const { - MineProgress ret; - ReadGuard l(x_localMiners); - for (auto& m: m_localMiners) - ret.combine(m.miningProgress()); - return ret; + return MiningProgress(); } uint64_t Client::hashrate() const { - uint64_t ret = 0; - ReadGuard l(x_localMiners); - for (LocalMiner const& m: m_localMiners) - ret += m.miningProgress().hashes / m.miningProgress().ms; - return ret / 1000; + return 0; } std::list Client::miningHistory() { std::list ret; - - ReadGuard l(x_localMiners); +/* ReadGuard l(x_localMiners); if (m_localMiners.empty()) return ret; ret = m_localMiners[0].miningHistory(); @@ -353,11 +338,11 @@ std::list Client::miningHistory() auto li = l.begin(); for (; ri != ret.end() && li != l.end(); ++ri, ++li) ri->combine(*li); - } + }*/ return ret; } -void Client::setupState(State& _s) +/*void Client::setupState(State& _s) { { ReadGuard l(x_stateDB); @@ -378,7 +363,7 @@ void Client::setupState(State& _s) } else _s.commitToMine(m_bc); -} +}*/ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice, Address const& _from) { @@ -406,15 +391,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 ProofOfWork::WorkPackage Client::getWork() { - Guard l(x_remoteMiner); - BlockInfo bi; - { - ReadGuard l(x_stateDB); - m_remoteMiner.update(m_postMine, m_bc); - m_postMine.commitToMine(m_bc); - bi = m_postMine.info(); - } - return ProofOfWork::package(bi); + return ProofOfWork::package(m_miningInfo); } bool Client::submitWork(ProofOfWork::Solution const& _solution) @@ -422,7 +399,7 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) bytes newBlock; { WriteGuard l(x_stateDB); - if (!m_postMine.completeMine(_solution)) + if (!m_postMine.completeMine(_solution)) return false; newBlock = m_postMine.blockData(); } @@ -435,7 +412,7 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) void Client::syncBlockQueue() { - ImportResult ir; + ImportRoute ir; { WriteGuard l(x_stateDB); @@ -446,30 +423,33 @@ void Client::syncBlockQueue() tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); x_stateDB.lock(); - if (fresh.size()) - m_stateDB = db; + if (ir.first.empty()) + return; + m_stateDB = db; } - - if (!ir.first.empty()) - onChainChanged(ir); - return true; + onChainChanged(ir); } void Client::syncTransactionQueue() { // returns TransactionReceipts, once for each transaction. cwork << "postSTATE <== TQ"; + + h256Set changeds; TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); if (newPendingReceipts.size()) { for (size_t i = 0; i < newPendingReceipts.size(); i++) appendFromNewPending(newPendingReceipts[i], changeds, m_postMine.pending()[i].sha3()); - changeds.insert(PendingChangedFilter); - if (isMining()) - cnote << "Additional transaction ready: Restarting mining operation."; - resyncStateNeeded = true; + // TODO: Tell farm about new transaction (i.e. restartProofOfWork mining). + onPostStateChanged(); + + // Tell watches about the new transactions. + noteChanged(changeds); + + // Tell network about the new transactions. if (auto h = m_host.lock()) h->noteNewTransactions(); } @@ -504,27 +484,41 @@ void Client::onChainChanged(ImportRoute const& _ir) h256Set changeds; for (auto const& h: _ir.first) - if (h != _ir.second) - appendFromNewBlock(h, changeds); + appendFromNewBlock(h, changeds); changeds.insert(ChainChangedFilter); - noteChanged(changeds); // RESTART MINING - // LOCKS NEEDED? - Guard l(x_stateDB); - cwork << "preSTATE <== CHAIN"; - if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) + // LOCKS REALLY NEEDED? { - if (isMining()) - cnote << "New block on chain: Restarting mining operation."; - m_postMine = m_preMine; - resyncStateNeeded = true; - changeds.insert(PendingChangedFilter); + ReadGuard l(x_stateDB); + if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) + { + if (isMining()) + cnote << "New block on chain."; + + m_postMine = m_preMine; + changeds.insert(PendingChangedFilter); + + x_stateDB.unlock(); + onPostStateChanged(); + x_stateDB.lock(); + } + } + + noteChanged(changeds); +} +void Client::onPostStateChanged() +{ + cnote << "Post state changed: Restarting mining..."; + { + WriteGuard l(x_stateDB); m_postMine.commitToMine(m_bc); - m_farm.setWork(m_postMine.info()); + m_miningInfo = m_postMine.info(); } + + m_farm.setWork(m_miningInfo); } void Client::noteChanged(h256Set const& _filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index 9f6bbfeb6..2235c4459 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -40,7 +40,6 @@ #include "TransactionQueue.h" #include "State.h" #include "CommonNet.h" -#include "Miner.h" #include "ABI.h" #include "Farm.h" #include "ClientBase.h" @@ -103,8 +102,6 @@ struct ClientDetail: public LogChannel { static const char* name() { return " C */ class Client: public ClientBase, Worker { - friend class OldMiner; - public: /// New-style Constructor. explicit Client( @@ -254,6 +251,10 @@ private: /// Magically called when m_tq needs syncing. Be nice and don't block. void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; } + /// Called when the post state has changed (i.e. when more transactions are in it or we're mining on a new block). + /// This updates m_miningInfo. + void onPostStateChanged(); + void checkWatchGarbage(); VersionChecker m_vc; ///< Dummy object to check & update the protocol version. @@ -265,12 +266,11 @@ private: OverlayDB m_stateDB; ///< Acts as the central point for the state database, so multiple States can share it. State m_preMine; ///< The present state of the client. State m_postMine; ///< The state of the client which we're mining (i.e. it'll have all the rewards added). + BlockInfo m_miningInfo; ///< The header we're attempting to mine on (derived from m_postMine). std::weak_ptr m_host; ///< Our Ethereum Host. Don't do anything if we can't lock. GenericFarm m_farm; ///< Our mining farm. - mutable Mutex x_remoteMiner; ///< The remote miner lock. - RemoteMiner m_remoteMiner; ///< The remote miner. Handler m_tqReady; Handler m_bqReady; diff --git a/libethereum/ClientBase.cpp b/libethereum/ClientBase.cpp index 5a0aef7c3..ecc0fb7f5 100644 --- a/libethereum/ClientBase.cpp +++ b/libethereum/ClientBase.cpp @@ -20,10 +20,12 @@ * @date 2015 */ -#include #include "ClientBase.h" + +#include #include "BlockChain.h" #include "Executive.h" +#include "State.h" using namespace std; using namespace dev; diff --git a/libethereum/ClientBase.h b/libethereum/ClientBase.h index 4b3cc5002..15dbbf1ab 100644 --- a/libethereum/ClientBase.h +++ b/libethereum/ClientBase.h @@ -25,6 +25,7 @@ #include #include "Interface.h" #include "LogFilter.h" +#include "TransactionQueue.h" namespace dev { @@ -60,15 +61,15 @@ struct ClientWatch }; struct WatchChannel: public LogChannel { static const char* name() { return "(o)"; } static const int verbosity = 7; }; -#define cwatch dev::LogOutputStream() +#define cwatch LogOutputStream() struct WorkInChannel: public LogChannel { static const char* name() { return ">W>"; } static const int verbosity = 16; }; struct WorkOutChannel: public LogChannel { static const char* name() { return "() -#define cworkin dev::LogOutputStream() -#define cworkout dev::LogOutputStream() +#define cwork LogOutputStream() +#define cworkin LogOutputStream() +#define cworkout LogOutputStream() -class ClientBase: public dev::eth::Interface +class ClientBase: public Interface { public: ClientBase() {} @@ -110,18 +111,18 @@ public: virtual LocalisedLogEntries checkWatch(unsigned _watchId) override; virtual h256 hashFromNumber(BlockNumber _number) const override; - virtual eth::BlockInfo blockInfo(h256 _hash) const override; - virtual eth::BlockDetails blockDetails(h256 _hash) const override; - virtual eth::Transaction transaction(h256 _transactionHash) const override; - virtual eth::Transaction transaction(h256 _blockHash, unsigned _i) const override; - virtual eth::Transactions transactions(h256 _blockHash) const override; - virtual eth::TransactionHashes transactionHashes(h256 _blockHash) const override; - virtual eth::BlockInfo uncle(h256 _blockHash, unsigned _i) const override; - virtual eth::UncleHashes uncleHashes(h256 _blockHash) const override; + virtual BlockInfo blockInfo(h256 _hash) const override; + virtual BlockDetails blockDetails(h256 _hash) const override; + virtual Transaction transaction(h256 _transactionHash) const override; + virtual Transaction transaction(h256 _blockHash, unsigned _i) const override; + virtual Transactions transactions(h256 _blockHash) const override; + virtual TransactionHashes transactionHashes(h256 _blockHash) const override; + virtual BlockInfo uncle(h256 _blockHash, unsigned _i) const override; + virtual UncleHashes uncleHashes(h256 _blockHash) const override; virtual unsigned transactionCount(h256 _blockHash) const override; virtual unsigned uncleCount(h256 _blockHash) const override; virtual unsigned number() const override; - virtual eth::Transactions pending() const override; + virtual Transactions pending() const override; virtual h256s pendingHashes() const override; void injectBlock(bytes const& _block); @@ -142,13 +143,13 @@ public: /// TODO: consider moving it to a separate interface - virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::startMining")); } - virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::stopMining")); } - virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::isMining")); } - virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::hashrate")); } - virtual eth::MineProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::miningProgress")); } - virtual std::pair getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::getWork")); } - virtual bool submitWork(eth::ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("dev::eth::ClientBase::submitWork")); } + virtual void startMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::startMining")); } + virtual void stopMining() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::stopMining")); } + virtual bool isMining() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::isMining")); } + virtual uint64_t hashrate() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::hashrate")); } + virtual MiningProgress miningProgress() const override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::miningProgress")); } + virtual ProofOfWork::WorkPackage getWork() override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::getWork")); } + virtual bool submitWork(ProofOfWork::Solution const&) override { BOOST_THROW_EXCEPTION(InterfaceNotSupported("ClientBase::submitWork")); } State asOf(BlockNumber _h) const; diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 137c137d3..09c7f0e78 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -29,6 +29,7 @@ #include #include #include +#include namespace dev { @@ -45,17 +46,25 @@ template class GenericFarm: public GenericFarmFace { public: + using WorkPackage = typename PoW::WorkPackage; + using Solution = typename PoW::Solution; + using Miner = GenericMiner; + /** * @brief Sets the current mining mission. * @param _bi The block (header) we wish to be mining. */ void setWork(BlockInfo const& _bi) { - WriteGuard l(x_work); - m_header = _bi; - m_work = PoW::package(m_header); - ReadGuard l(x_miners); - for (auto const& m: miners) + WorkPackage w; + { + WriteGuard l(x_work); + m_header = _bi; + w = m_work = PoW::package(m_header); + } + + ReadGuard l2(x_miners); + for (auto const& m: m_miners) m->setWork(m_work); } @@ -63,13 +72,13 @@ public: * @brief (Re)start miners for CPU only. * @returns true if started properly. */ - bool startCPU() { return start(); } + bool startCPU() { return start(); } /** * @brief (Re)start miners for GPU only. * @returns true if started properly. */ - bool startGPU() { start(); } + bool startGPU() { return start(); } /** * @brief Stop all mining activities. @@ -92,12 +101,14 @@ public: */ MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; } + using SolutionFound = std::function; + /** * @brief Provides a valid header based upon that received previously with setWork(). * @param _bi The now-valid header. * @return true if the header was good and that the Farm should pause until more work is submitted. */ - void onSolutionFound(function _handler) { m_onSolutionFound = _handler; } + void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } private: /** @@ -116,7 +127,7 @@ private: ReadGuard l(x_miners); for (std::shared_ptr const& m: m_miners) if (m.get() != _m) - m->pause(); + m->setWork(); m_work.headerHash = h256(); return true; } @@ -135,7 +146,7 @@ private: m_miners.clear(); m_miners.reserve(MinerType::instances()); for (unsigned i = 0; i < MinerType::instances(); ++i) - m_miners.push_back(new MinerType(std::make_pair(this, i))); + m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); return true; } @@ -149,7 +160,7 @@ private: WorkPackage m_work; BlockInfo m_header; - function m_onSolutionFound; + SolutionFound m_onSolutionFound; }; } diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 0ab81728b..b72a29c00 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -183,7 +183,7 @@ public: virtual uint64_t hashrate() const = 0; /// Get hash of the current block to be mined minus the nonce (the 'work hash'). - virtual std::pair getWork() = 0; + virtual ProofOfWork::WorkPackage getWork() = 0; /// Submit the nonce for the proof-of-work. virtual bool submitWork(ProofOfWork::Solution const& _proof) = 0; diff --git a/libethereum/Miner.cpp b/libethereum/Miner.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/libethereum/Miner.h b/libethereum/Miner.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/libweb3jsonrpc/WebThreeStubServerBase.cpp b/libweb3jsonrpc/WebThreeStubServerBase.cpp index 7e7b83e3c..65fb71a5c 100644 --- a/libweb3jsonrpc/WebThreeStubServerBase.cpp +++ b/libweb3jsonrpc/WebThreeStubServerBase.cpp @@ -758,8 +758,9 @@ Json::Value WebThreeStubServerBase::eth_getWork() { Json::Value ret(Json::arrayValue); auto r = client()->getWork(); - ret.append(toJS(r.first)); - ret.append(toJS(r.second)); + ret.append(toJS(r.headerHash)); + ret.append(toJS(r.seedHash)); + ret.append(toJS(r.boundary)); return ret; } diff --git a/libwebthree/WebThree.cpp b/libwebthree/WebThree.cpp index 8ea2133f0..bbe0d55ec 100644 --- a/libwebthree/WebThree.cpp +++ b/libwebthree/WebThree.cpp @@ -42,7 +42,7 @@ WebThreeDirect::WebThreeDirect( WithExisting _we, std::set const& _interfaces, NetworkPreferences const& _n, - bytesConstRef _network, int _miners + bytesConstRef _network ): m_clientVersion(_clientVersion), m_net(_clientVersion, _n, _network) @@ -50,7 +50,7 @@ WebThreeDirect::WebThreeDirect( if (_dbPath.size()) Defaults::setDBPath(_dbPath); if (_interfaces.count("eth")) - m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0, _miners)); + m_ethereum.reset(new eth::Client(&m_net, _dbPath, _we, 0)); if (_interfaces.count("shh")) m_whisper = m_net.registerCapability(new WhisperHost); diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h index 87cf62d4a..90a4aa3c2 100644 --- a/libwebthree/WebThree.h +++ b/libwebthree/WebThree.h @@ -112,8 +112,7 @@ public: WithExisting _we = WithExisting::Trust, std::set const& _interfaces = {"eth", "shh"}, p2p::NetworkPreferences const& _n = p2p::NetworkPreferences(), - bytesConstRef _network = bytesConstRef(), - int _miners = -1 + bytesConstRef _network = bytesConstRef() ); /// Destructor. From cd4478e4cf87963ca488590072b4b26f385a7d22 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 18:12:05 +0200 Subject: [PATCH 26/34] Various fixes for mining. --- alethzero/DownloadView.h | 1 - alethzero/MainWin.cpp | 10 ++-- alethzero/MiningView.cpp | 11 ++-- alethzero/MiningView.h | 4 +- eth/main.cpp | 17 +++--- exp/main.cpp | 54 +++++++++++++++---- libdevcore/Worker.h | 3 ++ libethcore/Ethash.cpp | 52 +++++++++--------- libethcore/Ethash.h | 22 +++++--- libethcore/Miner.h | 35 ++++++------ libethereum/Farm.h | 45 ++++++++++++---- mix/MixClient.cpp | 23 +++++--- mix/MixClient.h | 4 +- neth/main.cpp | 10 +--- test/TestHelper.cpp | 31 +++++++++++ test/TestHelper.h | 5 +- test/blockchain.cpp | 111 ++++++++++++++------------------------- test/dagger.cpp | 12 ++--- test/stateOriginal.cpp | 11 ++-- third/MainWin.cpp | 2 +- 20 files changed, 269 insertions(+), 194 deletions(-) diff --git a/alethzero/DownloadView.h b/alethzero/DownloadView.h index 22a11651c..d0fc445f8 100644 --- a/alethzero/DownloadView.h +++ b/alethzero/DownloadView.h @@ -32,7 +32,6 @@ #endif namespace dev { namespace eth { -struct MineInfo; class DownloadMan; }} diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 03d216a17..2e4478594 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -45,7 +45,7 @@ #endif #include #include -#include +#include #include #include #include @@ -164,7 +164,7 @@ Main::Main(QWidget *parent) : statusBar()->addPermanentWidget(ui->chainStatus); statusBar()->addPermanentWidget(ui->blockCount); - ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(ProofOfWork::name()).arg(ProofOfWork::revision()).arg(dev::Version)); + ui->blockCount->setText(QString("PV%2 D%3 %4-%5 v%6").arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(QString::fromStdString(ProofOfWork::name())).arg(ProofOfWork::revision()).arg(dev::Version)); connect(ui->ourAccounts->model(), SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), SLOT(ourAccountsRowsMoved())); @@ -952,7 +952,7 @@ void Main::on_preview_triggered() void Main::refreshMining() { - MineProgress p = ethereum()->miningProgress(); + MiningProgress p = ethereum()->miningProgress(); ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); if (!ui->miningView->isVisible()) return; @@ -1481,7 +1481,7 @@ void Main::on_blocks_currentItemChanged() s << "
Difficulty: " << info.difficulty << "" << "
"; if (info.number) { - auto e = Ethasher::eval(info); + auto e = EthashAux::eval(info); s << "
Proof-of-Work: " << e.value << " <= " << (h256)u256((bigint(1) << 256) / info.difficulty) << " (mixhash: " << e.mixHash.abridged() << ")" << "
"; s << "
Parent: " << info.parentHash << "" << "
"; } @@ -1510,7 +1510,7 @@ void Main::on_blocks_currentItemChanged() s << line << "Nonce: " << uncle.nonce << "" << ""; s << line << "Hash w/o nonce: " << uncle.headerHash(WithoutNonce) << "" << ""; s << line << "Difficulty: " << uncle.difficulty << "" << ""; - auto e = Ethasher::eval(uncle); + auto e = EthashAux::eval(uncle); s << line << "Proof-of-Work: " << e.value << " <= " << (h256)u256((bigint(1) << 256) / uncle.difficulty) << " (mixhash: " << e.mixHash.abridged() << ")" << ""; } if (info.parentHash) diff --git a/alethzero/MiningView.cpp b/alethzero/MiningView.cpp index 63d1fcf99..e020408ea 100644 --- a/alethzero/MiningView.cpp +++ b/alethzero/MiningView.cpp @@ -36,7 +36,7 @@ using namespace dev::eth; // types using dev::eth::MineInfo; -using dev::eth::MineProgress; +using dev::eth::MiningProgress; // functions using dev::toString; @@ -50,12 +50,13 @@ MiningView::MiningView(QWidget* _p): QWidget(_p) { } -void MiningView::appendStats(list const& _i, MineProgress const& _p) +void MiningView::appendStats(list const& _i, MiningProgress const& _p) { + (void)_p; if (_i.empty()) return; - unsigned o = m_values.size(); +/* unsigned o = m_values.size(); for (MineInfo const& i: _i) { m_values.push_back(i.best); @@ -91,7 +92,7 @@ void MiningView::appendStats(list const& _i, MineProgress const& _p) m_completes.erase(remove_if(m_completes.begin(), m_completes.end(), [](int i){return i < 0;}), m_completes.end()); m_progress = _p; - update(); + update();*/ } void MiningView::resetStats() @@ -101,6 +102,7 @@ void MiningView::resetStats() void MiningView::paintEvent(QPaintEvent*) { + /* Grapher g; QPainter p(this); @@ -115,4 +117,5 @@ void MiningView::paintEvent(QPaintEvent*) g.ruleY(r - 1, QColor(128, 128, 128)); for (auto r: m_completes) g.ruleY(r, QColor(192, 64, 64)); + */ } diff --git a/alethzero/MiningView.h b/alethzero/MiningView.h index 8f3135f75..65b9f2ec9 100644 --- a/alethzero/MiningView.h +++ b/alethzero/MiningView.h @@ -42,14 +42,14 @@ class MiningView: public QWidget public: MiningView(QWidget* _p = nullptr); - void appendStats(std::list const& _l, dev::eth::MineProgress const& _p); + void appendStats(std::list const& _l, dev::eth::MiningProgress const& _p); void resetStats(); protected: virtual void paintEvent(QPaintEvent*); private: - dev::eth::MineProgress m_progress; + dev::eth::MiningProgress m_progress; unsigned m_duration = 300; std::vector m_values; std::vector m_bests; diff --git a/eth/main.cpp b/eth/main.cpp index 66abefd54..24bf839aa 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -126,13 +126,16 @@ void help() #if ETH_JSONRPC << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: " << SensibleHttpPort << ")." << endl +#endif +#if ETH_EVMJIT + << " -J,--jit Enable EVM JIT (default: off)." << endl #endif << " -K,--kill First kill the blockchain." << endl << " --listen-ip Listen on the given port for incoming connections (default: 30303)." << endl << " -l,--listen Listen on the given IP for incoming connections (default: 0.0.0.0)." << endl << " -u,--public-ip Force public ip to given (default: auto)." << endl << " -m,--mining Enable mining, optionally for a specified number of blocks (Default: off)" << endl - << " -n,--upnp Use upnp for NAT (default: on)." << endl + << " -n,-u,--upnp Use upnp for NAT (default: on)." << endl << " -o,--mode Start a full node or a peer node (Default: full)." << endl << " -p,--port Connect to remote port (default: 30303)." << endl << " -P,--priority <0 - 100> Default % priority of a transaction (default: 50)." << endl @@ -140,7 +143,6 @@ void help() << " -r,--remote Connect to remote host (default: none)." << endl << " -s,--secret Set the secret key for use with send command (default: auto)." << endl << " -S,--temporary-secret Set the secret key for use with send command, for this session only." << endl - << " -t,--miners Number of mining threads to start (Default: " << thread::hardware_concurrency() << ")" << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl << " -V,--version Show the version and exit." << endl @@ -336,7 +338,7 @@ int main(int argc, char** argv) exportFrom = argv[++i]; else if (arg == "--only" && i + 1 < argc) exportTo = exportFrom = argv[++i]; - else if ((arg == "-n" || arg == "--upnp") && i + 1 < argc) + else if ((arg == "-n" || arg == "-u" || arg == "--upnp") && i + 1 < argc) { string m = argv[++i]; if (isTrue(m)) @@ -490,15 +492,12 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "--jit") - { #if ETH_EVMJIT + else if (arg == "-J" || arg == "--jit") + { jit = true; -#else - cerr << "EVM JIT not enabled" << endl; - return -1; -#endif } +#endif else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") diff --git a/exp/main.cpp b/exp/main.cpp index 88f1075a9..933fda1a6 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -109,18 +109,50 @@ int main() #else int main() { -#if ETH_ETHASHCL - EthashCL ecl; + GenericFarm f; BlockInfo genesis = CanonBlockChain::genesis(); genesis.difficulty = 1 << 18; - cdebug << (h256)u256((bigint(1) << 256) / genesis.difficulty); - std::pair r; - while (!r.first.completed) - r = ecl.mine(genesis, 1000); - cdebug << r.second.mixHash << r.second.nonce; - EthashCL::assignResult(r.second, genesis); - assert(EthashCPU::verify(genesis)); -#endif + cdebug << genesis.boundary(); + + auto mine = [](GenericFarm& f, BlockInfo const& g, unsigned timeout) { + BlockInfo bi = g; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + ProofOfWork::assignResult(sol, bi); + return completed = true; + }); + f.setWork(bi); + for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush) + this_thread::sleep_for(chrono::milliseconds(100)); + cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad"); + }; + + f.startCPU(); + mine(f, genesis, 10); + mine(f, genesis, 10); + f.startGPU(); + + cdebug << "Good:"; + genesis.difficulty = 1 << 18; + genesis.noteDirty(); + mine(f, genesis, 3); + + cdebug << "Bad:"; + genesis.difficulty = (u256(1) << 40); + genesis.noteDirty(); + mine(f, genesis, 3); + + cdebug << "Good:"; + genesis.difficulty = 1 << 18; + genesis.noteDirty(); + mine(f, genesis, 3); + + cdebug << "Bad:"; + genesis.difficulty = (u256(1) << 40); + genesis.noteDirty(); + mine(f, genesis, 3); + return 0; } #endif diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 6a35d6c4c..24ff4cc15 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -66,6 +66,9 @@ protected: /// Called when is to be stopped, just prior to thread being joined. virtual void doneWorking() {} + /// Blocks caller into worker thread has finished. + void join() const { Guard l(x_work); try { if (m_work) m_work->join(); } catch (...) {} } + private: std::string m_name; unsigned m_idleWaitMs = 0; diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 03c7a3654..f66188976 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -63,7 +63,7 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi) { WorkPackage ret; ret.boundary = _bi.boundary(); - ret.headerHash = _bi.headerHash(WithNonce); + ret.headerHash = _bi.headerHash(WithoutNonce); ret.seedHash = _bi.seedHash(); return ret; } @@ -95,9 +95,8 @@ bool Ethash::verify(BlockInfo const& _header) return false; #endif - h256 boundary = u256((bigint(1) << 256) / _header.difficulty); auto result = EthashAux::eval(_header); - bool slow = result.value <= boundary && result.mixHash == _header.mixHash; + bool slow = result.value <= _header.boundary() && result.mixHash == _header.mixHash; #if ETH_DEBUG || !ETH_TRUE if (!pre && slow) @@ -107,7 +106,7 @@ bool Ethash::verify(BlockInfo const& _header) cwarn << "nonce:" << _header.nonce; cwarn << "mixHash:" << _header.mixHash; cwarn << "difficulty:" << _header.difficulty; - cwarn << "boundary:" << boundary; + cwarn << "boundary:" << _header.boundary(); cwarn << "result.value:" << result.value; cwarn << "result.mixHash:" << result.mixHash; } @@ -125,16 +124,18 @@ void Ethash::CPUMiner::workLoop() ethash_return_value ethashReturn; auto p = EthashAux::params(m_work.seedHash); - void const* dagPointer = EthashAux::full(m_work.headerHash).data(); + void const* dagPointer = EthashAux::full(m_work.seedHash).data(); uint8_t const* headerHashPointer = m_work.headerHash.data(); h256 boundary = m_work.boundary; - unsigned hashCount = 0; + unsigned hashCount = 1; for (; !shouldStop(); tryNonce++, hashCount++) { ethash_compute_full(ðashReturn, dagPointer, &p, headerHashPointer, tryNonce); h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) break; + if (!(hashCount % 1000)) + accumulateHashes(1000); } } @@ -148,6 +149,7 @@ public: void abort() { Guard l(x_all); + m_owner->m_work.headerHash = h256(); if (m_aborted) return; // cdebug << "Attempting to abort"; @@ -159,8 +161,6 @@ public: m_aborted = m_abort = false; } - uint64_t fetchTotal() { Guard l(x_all); auto ret = m_total; m_total = 0; return ret; } - protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { @@ -180,7 +180,7 @@ protected: { Guard l(x_all); // cdebug << "Searched" << _count << "from" << _startNonce; - m_total += _count; + m_owner->accumulateHashes(_count); m_last = _startNonce + _count; if (m_abort) { @@ -192,7 +192,6 @@ protected: private: Mutex x_all; - uint64_t m_total; uint64_t m_last; bool m_abort = false; bool m_aborted = true; @@ -214,38 +213,43 @@ Ethash::GPUMiner::~GPUMiner() bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; - Result r = EthashAux::eval(m_lastWork.seedHash, m_lastWork.headerHash, n); - if (r.value < m_lastWork.boundary) + Result r = EthashAux::eval(m_work.seedHash, m_work.headerHash, n); + if (r.value < m_work.boundary) return submitProof(Solution{n, r.mixHash}); return false; } void Ethash::GPUMiner::kickOff(WorkPackage const& _work) { - if (!m_miner || m_minerSeed != _work.seedHash) + m_work = _work; + startWorking(); +} + +void Ethash::GPUMiner::workLoop() +{ + // take local copy of work since it may end up being overwritten by kickOff/pause. + WorkPackage w = m_work; + if (!m_miner || m_minerSeed != w.seedHash) { - if (m_miner) - m_hook->abort(); + m_minerSeed = w.seedHash; delete m_miner; m_miner = new ethash_cl_miner; - auto p = EthashAux::params(_work.seedHash); - auto cb = [&](void* d) { EthashAux::full(_work.seedHash, bytesRef((byte*)d, p.full_size)); }; + auto p = EthashAux::params(m_minerSeed); + auto cb = [&](void* d) { EthashAux::full(m_minerSeed, bytesRef((byte*)d, p.full_size)); }; m_miner->init(p, cb, 32); } - if (m_lastWork.headerHash != _work.headerHash) - { - m_hook->abort(); - uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)_work.boundary >> 192); - m_miner->search(_work.headerHash.data(), upper64OfBoundary, *m_hook); - } - m_lastWork = _work; + + uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); + m_miner->search(w.headerHash.data(), upper64OfBoundary, *m_hook); } void Ethash::GPUMiner::pause() { m_hook->abort(); + stopWorking(); + m_work.headerHash = h256(); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 1a7d82149..8f1ba3eb3 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -59,6 +59,11 @@ public: struct WorkPackage { + WorkPackage() = default; + + void reset() { headerHash = h256(); } + operator bool() const { return headerHash != h256(); } + h256 boundary; h256 headerHash; ///< When h256() means "pause until notified a new work package is available". h256 seedHash; @@ -89,17 +94,16 @@ public: startWorking(); } - void pause() override { stopWorking(); } + void pause() override { stopWorking(); m_work.reset(); } private: void workLoop() override; WorkPackage m_work; - MineInfo m_info; }; #if ETH_ETHASHCL || !ETH_TRUE - class GPUMiner: public Miner + class GPUMiner: public Miner, Worker { friend class dev::eth::EthashCLHook; @@ -114,14 +118,16 @@ public: void pause() override; private: + void workLoop() override; bool report(uint64_t _nonce); - EthashCLHook* m_hook; - ethash_cl_miner* m_miner; + using Miner::accumulateHashes; + + EthashCLHook* m_hook = nullptr; + ethash_cl_miner* m_miner = nullptr; - h256 m_minerSeed; - WorkPackage m_lastWork; ///< Work loaded into m_miner. - MineInfo m_info; + h256 m_minerSeed; ///< Last seed in m_miner + WorkPackage m_work; ///< Work to be done by GPU, set with kickOff and picked up in workLoop. }; #else using GPUMiner = CPUMiner; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index 9372c06b1..ea51b0eb5 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -34,30 +34,24 @@ namespace dev namespace eth { -struct MineInfo -{ - MineInfo() = default; - MineInfo(bool _completed): completed(_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; - unsigned hashes = 0; - bool completed = false; -}; - /** * @brief Describes the progress of a mining operation. */ struct MiningProgress { - void combine(MiningProgress 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. +// MiningProgress& operator+=(MiningProgress const& _mp) { hashes += _mp.hashes; ms = std::max(ms, _mp.ms); return *this; } unsigned hashes = 0; ///< Total number of hashes computed. unsigned ms = 0; ///< Total number of milliseconds of mining thus far. }; +struct MineInfo: public MiningProgress {}; + +inline std::ostream& operator<<(std::ostream& _out, MiningProgress const& _p) +{ + _out << (_p.hashes * 1000 / _p.ms) << "H/s = " << _p.hashes << " hashes / " << (double(_p.ms) / 1000) << "s"; + return _out; +} + template class GenericMiner; /** @@ -103,13 +97,18 @@ public: void setWork(WorkPackage const& _work = WorkPackage()) { Guard l(x_work); + if (_work.headerHash == m_work.headerHash) + return; if (_work.headerHash != h256()) - kickOff(m_work); + kickOff(_work); else if (m_work.headerHash == h256() && _work.headerHash != h256()) pause(); m_work = _work; + m_hashCount = 0; } + unsigned hashCount() { return m_hashCount; } + unsigned index() const { return m_index; } protected: @@ -146,12 +145,16 @@ protected: WorkPackage const& work() const { return m_work; } + void accumulateHashes(unsigned _n) { m_hashCount += _n; } + private: FarmFace* m_farm = nullptr; unsigned m_index; Mutex x_work; WorkPackage m_work; + + unsigned m_hashCount = 0; }; } diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 09c7f0e78..c20c27e6d 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -56,16 +56,16 @@ public: */ void setWork(BlockInfo const& _bi) { - WorkPackage w; - { - WriteGuard l(x_work); - m_header = _bi; - w = m_work = PoW::package(m_header); - } - + WriteGuard l(x_work); ReadGuard l2(x_miners); + m_header = _bi; + auto p = PoW::package(m_header); + if (p.headerHash == m_work.headerHash) + return; + m_work = p; for (auto const& m: m_miners) m->setWork(m_work); + resetTimer(); } /** @@ -99,7 +99,19 @@ public: * @brief Get information on the progress of mining this work package. * @return The progress with mining so far. */ - MiningProgress const& miningProgress() const { ReadGuard l(x_progress); return m_progress; } + MiningProgress const& miningProgress() const + { + MiningProgress p; + p.ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_lastStart).count(); + { + ReadGuard l2(x_miners); + for (auto const& i: m_miners) + p.hashes += i->hashCount(); + } + ReadGuard l(x_progress); + m_progress = p; + return m_progress; + } using SolutionFound = std::function; @@ -110,6 +122,8 @@ public: */ void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } + WorkPackage work() const { ReadGuard l(x_work); return m_work; } + private: /** * @brief Called from a Miner to note a WorkPackage has a solution. @@ -140,21 +154,32 @@ private: template bool start() { - WriteGuard l(x_miners); + ReadGuard l(x_work); + WriteGuard l2(x_miners); if (!m_miners.empty() && !!std::dynamic_pointer_cast(m_miners[0])) return true; m_miners.clear(); m_miners.reserve(MinerType::instances()); for (unsigned i = 0; i < MinerType::instances(); ++i) + { m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); + m_miners.back()->setWork(m_work); + } + resetTimer(); return true; } + void resetTimer() + { + m_lastStart = std::chrono::steady_clock::now(); + } + mutable SharedMutex x_miners; std::vector> m_miners; mutable SharedMutex x_progress; - MiningProgress m_progress; + mutable MiningProgress m_progress; + std::chrono::steady_clock::time_point m_lastStart; mutable SharedMutex x_work; WorkPackage m_work; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 2e8bd5384..76552fb06 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -20,6 +20,7 @@ * Ethereum IDE client. */ +#include "MixClient.h" #include #include #include @@ -28,10 +29,8 @@ #include #include #include - #include "Exceptions.h" -#include "MixClient.h" - +using namespace std; using namespace dev; using namespace dev::eth; @@ -250,9 +249,17 @@ void MixClient::mine() { WriteGuard l(x_state); m_state.commitToMine(bc()); - ProofOfWork pow; - while (!m_state.mine(&pow).completed) {} - m_state.completeMine(); + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + return completed = m_state.completeMine(sol); + }); + f.setWork(m_state.info()); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); + bc().import(m_state.blockData(), m_stateDB); m_state.sync(bc()); m_startState = m_state; @@ -392,9 +399,9 @@ uint64_t MixClient::hashrate() const return 0; } -eth::MineProgress MixClient::miningProgress() const +eth::MiningProgress MixClient::miningProgress() const { - return eth::MineProgress(); + return eth::MiningProgress(); } } diff --git a/mix/MixClient.h b/mix/MixClient.h index 649c7694f..c496df754 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -67,8 +67,8 @@ public: void stopMining() override; bool isMining() const override; uint64_t hashrate() const override; - eth::MineProgress miningProgress() const override; - std::pair getWork() override { return std::pair(); } + eth::MiningProgress miningProgress() const override; + eth::ProofOfWork::WorkPackage getWork() override { return eth::ProofOfWork::WorkPackage(); } bool submitWork(eth::ProofOfWork::Solution const&) override { return false; } virtual void flushTransactions() override {} diff --git a/neth/main.cpp b/neth/main.cpp index fd3d3f403..4c38da0a4 100644 --- a/neth/main.cpp +++ b/neth/main.cpp @@ -40,7 +40,6 @@ #include #include #endif -#include #include "BuildInfo.h" #undef KEY_EVENT // from windows.h @@ -332,7 +331,6 @@ int main(int argc, char** argv) unsigned mining = ~(unsigned)0; NodeMode mode = NodeMode::Full; unsigned peers = 5; - int miners = -1; #if ETH_JSONRPC int jsonrpc = 8080; #endif @@ -502,8 +500,6 @@ int main(int argc, char** argv) g_logVerbosity = atoi(argv[++i]); else if ((arg == "-x" || arg == "--peers") && i + 1 < argc) peers = atoi(argv[++i]); - else if ((arg == "-t" || arg == "--miners") && i + 1 < argc) - miners = atoi(argv[++i]); else if ((arg == "-o" || arg == "--mode") && i + 1 < argc) { string m = argv[++i]; @@ -553,9 +549,7 @@ int main(int argc, char** argv) killChain ? WithExisting::Kill : WithExisting::Trust, mode == NodeMode::Full ? set{"eth", "shh"} : set(), netPrefs, - &nodesState, - miners - ); + &nodesState); web3.setIdealPeerCount(peers); std::shared_ptr gasPricer = make_shared(u256(double(ether / 1000) / etherPrice), u256(blockFees * 1000)); @@ -1253,7 +1247,7 @@ int main(int argc, char** argv) if (c && c->isMining()) { mvwprintw(consolewin, qheight - 1, width / 4 - 11, "Mining ON"); - dev::eth::MineProgress p = c->miningProgress(); + dev::eth::MiningProgress p = c->miningProgress(); auto speed = boost::format("%2% kH/s @ %1%s") % (p.ms / 1000) % (p.ms ? p.hashes / p.ms : 0); mvwprintw(consolewin, qheight - 2, width / 4 - speed.str().length() - 2, speed.str().c_str()); } diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index 45fe55b07..93c564e62 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -62,6 +62,37 @@ void connectClients(Client& c1, Client& c2) c2.connect("127.0.0.1", c1Port); #endif } + +void mine(State& s, BlockChain const& _bc) +{ + s.commitToMine(_bc); + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + return completed = s.completeMine(sol); + }); + f.setWork(s.info()); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); +} + +void mine(BlockInfo& _bi) +{ + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + ProofOfWork::assignResult(sol, _bi); + return completed = true; + }); + f.setWork(_bi); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); +} + } namespace test diff --git a/test/TestHelper.h b/test/TestHelper.h index 04ca95be4..92745bc36 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -36,9 +36,12 @@ namespace eth { class Client; +class State; void mine(Client& c, int numBlocks); void connectClients(Client& c1, Client& c2); +void mine(State& _s, BlockChain const& _bc); +void mine(BlockInfo& _bi); } @@ -225,7 +228,5 @@ public: }; }; - - } } diff --git a/test/blockchain.cpp b/test/blockchain.cpp index 8f5605898..4aa70c63a 100644 --- a/test/blockchain.cpp +++ b/test/blockchain.cpp @@ -191,11 +191,7 @@ void doBlockchainTests(json_spirit::mValue& _v, bool _fillin) { state.sync(bc); state.sync(bc, txs, gp); - state.commitToMine(bc); - MineInfo info; - ProofOfWork pow; - for (info.completed = false; !info.completed; info = state.mine(&pow)) {} - state.completeMine(); + mine(state, bc); } catch (Exception const& _e) { @@ -531,76 +527,55 @@ bytes createBlockRLPFromFields(mObject& _tObj) return rlpStream.out(); } -void overwriteBlockHeader(BlockInfo& _currentBlockHeader, mObject& _blObj) +void overwriteBlockHeader(BlockInfo& _header, mObject& _blObj) { - if (_blObj["blockHeader"].get_obj().size() != 14) + auto ho = _blObj["blockHeader"].get_obj(); + if (ho.size() != 14) { - - BlockInfo tmp = _currentBlockHeader; - - if (_blObj["blockHeader"].get_obj().count("parentHash")) - tmp.parentHash = h256(_blObj["blockHeader"].get_obj()["parentHash"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("uncleHash")) - tmp.sha3Uncles = h256(_blObj["blockHeader"].get_obj()["uncleHash"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("coinbase")) - tmp.coinbaseAddress = Address(_blObj["blockHeader"].get_obj()["coinbase"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("stateRoot")) - tmp.stateRoot = h256(_blObj["blockHeader"].get_obj()["stateRoot"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("transactionsTrie")) - tmp.transactionsRoot = h256(_blObj["blockHeader"].get_obj()["transactionsTrie"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("receiptTrie")) - tmp.receiptsRoot = h256(_blObj["blockHeader"].get_obj()["receiptTrie"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("bloom")) - tmp.logBloom = LogBloom(_blObj["blockHeader"].get_obj()["bloom"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("difficulty")) - tmp.difficulty = toInt(_blObj["blockHeader"].get_obj()["difficulty"]); - - if (_blObj["blockHeader"].get_obj().count("number")) - tmp.number = toInt(_blObj["blockHeader"].get_obj()["number"]); - - if (_blObj["blockHeader"].get_obj().count("gasLimit")) - tmp.gasLimit = toInt(_blObj["blockHeader"].get_obj()["gasLimit"]); - - if (_blObj["blockHeader"].get_obj().count("gasUsed")) - tmp.gasUsed = toInt(_blObj["blockHeader"].get_obj()["gasUsed"]); - - if (_blObj["blockHeader"].get_obj().count("timestamp")) - tmp.timestamp = toInt(_blObj["blockHeader"].get_obj()["timestamp"]); - - if (_blObj["blockHeader"].get_obj().count("extraData")) - tmp.extraData = importByteArray(_blObj["blockHeader"].get_obj()["extraData"].get_str()); - - if (_blObj["blockHeader"].get_obj().count("mixHash")) - tmp.mixHash = h256(_blObj["blockHeader"].get_obj()["mixHash"].get_str()); + BlockInfo tmp = _header; + if (ho.count("parentHash")) + tmp.parentHash = h256(ho["parentHash"].get_str()); + if (ho.count("uncleHash")) + tmp.sha3Uncles = h256(ho["uncleHash"].get_str()); + if (ho.count("coinbase")) + tmp.coinbaseAddress = Address(ho["coinbase"].get_str()); + if (ho.count("stateRoot")) + tmp.stateRoot = h256(ho["stateRoot"].get_str()); + if (ho.count("transactionsTrie")) + tmp.transactionsRoot = h256(ho["transactionsTrie"].get_str()); + if (ho.count("receiptTrie")) + tmp.receiptsRoot = h256(ho["receiptTrie"].get_str()); + if (ho.count("bloom")) + tmp.logBloom = LogBloom(ho["bloom"].get_str()); + if (ho.count("difficulty")) + tmp.difficulty = toInt(ho["difficulty"]); + if (ho.count("number")) + tmp.number = toInt(ho["number"]); + if (ho.count("gasLimit")) + tmp.gasLimit = toInt(ho["gasLimit"]); + if (ho.count("gasUsed")) + tmp.gasUsed = toInt(ho["gasUsed"]); + if (ho.count("timestamp")) + tmp.timestamp = toInt(ho["timestamp"]); + if (ho.count("extraData")) + tmp.extraData = importByteArray(ho["extraData"].get_str()); + if (ho.count("mixHash")) + tmp.mixHash = h256(ho["mixHash"].get_str()); + tmp.noteDirty(); // find new valid nonce - - if (tmp != _currentBlockHeader) + if (tmp != _header) { - _currentBlockHeader = tmp; - - ProofOfWork pow; - std::pair ret; - while (!ProofOfWork::verify(_currentBlockHeader)) - { - ret = pow.mine(_currentBlockHeader, 1000, true); - Ethash::assignResult(ret.second, _currentBlockHeader); - } + mine(tmp); + _header = tmp; } } else { // take the blockheader as is - const bytes c_blockRLP = createBlockRLPFromFields(_blObj["blockHeader"].get_obj()); + const bytes c_blockRLP = createBlockRLPFromFields(ho); const RLP c_bRLP(c_blockRLP); - _currentBlockHeader.populateFromHeader(c_bRLP, IgnoreNonce); + _header.populateFromHeader(c_bRLP, IgnoreNonce); } } @@ -631,13 +606,7 @@ BlockInfo constructBlock(mObject& _o) void updatePoW(BlockInfo& _bi) { - ProofOfWork pow; - std::pair ret; - while (!ProofOfWork::verify(_bi)) - { - ret = pow.mine(_bi, 10000, true); - Ethash::assignResult(ret.second, _bi); - } + mine(_bi); _bi.noteDirty(); } diff --git a/test/dagger.cpp b/test/dagger.cpp index 4abba5090..367c422ad 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -25,7 +25,7 @@ #include "JsonSpiritHeaders.h" #include #include -#include +#include #include #include "TestHelper.h" @@ -63,18 +63,18 @@ BOOST_AUTO_TEST_CASE(basic_test) unsigned cacheSize(o["cache_size"].get_int()); h256 cacheHash(o["cache_hash"].get_str()); - BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize); - BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->light(header), cacheSize)), cacheHash); + BOOST_REQUIRE_EQUAL(EthashAux::get()->params(header).cache_size, cacheSize); + BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)EthashAux::get()->light(header), cacheSize)), cacheHash); #if TEST_FULL unsigned fullSize(o["full_size"].get_int()); h256 fullHash(o["full_hash"].get_str()); - BOOST_REQUIRE_EQUAL(Ethasher::get()->full(header).size(), fullSize); - BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->full(header)), fullHash); + BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header).size(), fullSize); + BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)), fullHash); #endif h256 result(o["result"].get_str()); - Ethasher::Result r = Ethasher::eval(header); + Ethash::Result r = EthashAux::eval(header); BOOST_REQUIRE_EQUAL(r.value, result); BOOST_REQUIRE_EQUAL(r.mixHash, header.mixHash); } diff --git a/test/stateOriginal.cpp b/test/stateOriginal.cpp index 40f759434..e1a3c7c4a 100644 --- a/test/stateOriginal.cpp +++ b/test/stateOriginal.cpp @@ -25,7 +25,9 @@ #include #include #include +#include #include +#include "TestHelper.h" using namespace std; using namespace dev; using namespace dev::eth; @@ -67,10 +69,8 @@ BOOST_AUTO_TEST_CASE(Complex) cout << s; // Mine to get some ether! - s.commitToMine(bc); - ProofOfWork pow; - while (!s.mine(&pow).completed) {} - s.completeMine(); + mine(s, bc); + bc.attemptImport(s.blockData(), stateDB); cout << bc; @@ -89,8 +89,7 @@ BOOST_AUTO_TEST_CASE(Complex) // Mine to get some ether and set in stone. s.commitToMine(bc); s.commitToMine(bc); - while (!s.mine(&pow).completed) {} - s.completeMine(); + mine(s, bc); bc.attemptImport(s.blockData(), stateDB); cout << bc; diff --git a/third/MainWin.cpp b/third/MainWin.cpp index b03723131..12625ffbc 100644 --- a/third/MainWin.cpp +++ b/third/MainWin.cpp @@ -465,7 +465,7 @@ void Main::on_urlEdit_returnPressed() void Main::refreshMining() { - dev::eth::MineProgress p = ethereum()->miningProgress(); + dev::eth::MiningProgress p = ethereum()->miningProgress(); ui->mineStatus->setText(ethereum()->isMining() ? QString("%1s @ %2kH/s").arg(p.ms / 1000).arg(p.ms ? p.hashes / p.ms : 0) : "Not mining"); } From e5ac73bf0fddf6d0a803ed99f14217f10bc573fa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 21:36:55 +0200 Subject: [PATCH 27/34] Thread management fixes for Farm. --- exp/main.cpp | 21 +++++++++------------ libdevcore/Worker.cpp | 22 +++++++++++++++++----- libdevcore/Worker.h | 10 +++++++++- libethcore/Ethash.cpp | 36 +++++++++++++++++++++--------------- libethcore/Ethash.h | 10 +++------- libethcore/Miner.h | 31 ++++++++++++++++++------------- libethereum/Farm.h | 7 ++++++- 7 files changed, 83 insertions(+), 54 deletions(-) diff --git a/exp/main.cpp b/exp/main.cpp index 933fda1a6..2055c49e1 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -125,33 +125,30 @@ int main() f.setWork(bi); for (unsigned i = 0; !completed && i < timeout * 10; ++i, cout << f.miningProgress() << "\r" << flush) this_thread::sleep_for(chrono::milliseconds(100)); + cout << endl << flush; cdebug << bi.mixHash << bi.nonce << (Ethash::verify(bi) ? "GOOD" : "bad"); }; + Ethash::prep(genesis); + + genesis.difficulty = u256(1) << 40; + genesis.noteDirty(); f.startCPU(); mine(f, genesis, 10); - mine(f, genesis, 10); + f.startGPU(); cdebug << "Good:"; genesis.difficulty = 1 << 18; genesis.noteDirty(); - mine(f, genesis, 3); + mine(f, genesis, 30); cdebug << "Bad:"; genesis.difficulty = (u256(1) << 40); genesis.noteDirty(); - mine(f, genesis, 3); + mine(f, genesis, 30); - cdebug << "Good:"; - genesis.difficulty = 1 << 18; - genesis.noteDirty(); - mine(f, genesis, 3); - - cdebug << "Bad:"; - genesis.difficulty = (u256(1) << 40); - genesis.noteDirty(); - mine(f, genesis, 3); + f.stop(); return 0; } diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index bc8fe97f2..65e8efcbb 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -27,12 +27,20 @@ using namespace std; using namespace dev; -void Worker::startWorking() +void Worker::startWorking(IfRunning _ir) { cnote << "startWorking for thread" << m_name; Guard l(x_work); - if (m_work) - return; + + if (m_work && m_work->joinable()) + try { + if (_ir == IfRunning::Detach) + m_work->detach(); + else if (_ir == IfRunning::Join) + m_work->join(); + else + return; + } catch (...) {} cnote << "Spawning" << m_name; m_stop = false; m_work.reset(new thread([&]() @@ -40,6 +48,7 @@ void Worker::startWorking() setThreadName(m_name.c_str()); startedWorking(); workLoop(); + m_work->detach(); cnote << "Finishing up worker thread"; doneWorking(); })); @@ -49,11 +58,14 @@ void Worker::stopWorking() { cnote << "stopWorking for thread" << m_name; Guard l(x_work); - if (!m_work) + if (!m_work || !m_work->joinable()) return; cnote << "Stopping" << m_name; m_stop = true; - m_work->join(); + try { + m_work->join(); + } + catch (...) {} m_work.reset(); cnote << "Stopped" << m_name; } diff --git a/libdevcore/Worker.h b/libdevcore/Worker.h index 24ff4cc15..287ff6d6f 100644 --- a/libdevcore/Worker.h +++ b/libdevcore/Worker.h @@ -23,11 +23,19 @@ #include #include +#include #include "Guards.h" namespace dev { +enum class IfRunning +{ + Fail, + Join, + Detach +}; + class Worker { protected: @@ -45,7 +53,7 @@ protected: void setName(std::string _n) { if (!isWorking()) m_name = _n; } /// Starts worker thread; causes startedWorking() to be called. - void startWorking(); + void startWorking(IfRunning _ir = IfRunning::Fail); /// Stop worker thread; causes call to stopWorking(). void stopWorking(); diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index f66188976..3dd9d3b60 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -30,9 +30,10 @@ #include #include #include +#include +#include #include #include -#include #include #if ETH_ETHASHCL || !ETH_TRUE #include @@ -123,10 +124,12 @@ void Ethash::CPUMiner::workLoop() uint64_t tryNonce = (uint64_t)(u64)Nonce::random(s_eng); ethash_return_value ethashReturn; - auto p = EthashAux::params(m_work.seedHash); - void const* dagPointer = EthashAux::full(m_work.seedHash).data(); - uint8_t const* headerHashPointer = m_work.headerHash.data(); - h256 boundary = m_work.boundary; + WorkPackage w = work(); + + auto p = EthashAux::params(w.seedHash); + void const* dagPointer = EthashAux::full(w.seedHash).data(); + uint8_t const* headerHashPointer = w.headerHash.data(); + h256 boundary = w.boundary; unsigned hashCount = 1; for (; !shouldStop(); tryNonce++, hashCount++) { @@ -149,7 +152,6 @@ public: void abort() { Guard l(x_all); - m_owner->m_work.headerHash = h256(); if (m_aborted) return; // cdebug << "Attempting to abort"; @@ -158,13 +160,17 @@ public: std::this_thread::sleep_for(chrono::milliseconds(30)); // if (!m_aborted) // cwarn << "Couldn't abort. Abandoning OpenCL process."; + } + + void reset() + { m_aborted = m_abort = false; } protected: virtual bool found(uint64_t const* _nonces, uint32_t _count) override { -// cdebug << "Found nonces: " << vector(_nonces, _nonces + _count); +// dev::operator <<(std::cerr << "Found nonces: ", vector(_nonces, _nonces + _count)) << std::endl; for (uint32_t i = 0; i < _count; ++i) { if (m_owner->report(_nonces[i])) @@ -179,7 +185,7 @@ protected: virtual bool searched(uint64_t _startNonce, uint32_t _count) override { Guard l(x_all); -// cdebug << "Searched" << _count << "from" << _startNonce; +// std::cerr << "Searched " << _count << " from " << _startNonce << std::endl; m_owner->accumulateHashes(_count); m_last = _startNonce + _count; if (m_abort) @@ -206,29 +212,30 @@ Ethash::GPUMiner::GPUMiner(ConstructionInfo const& _ci): Ethash::GPUMiner::~GPUMiner() { - delete m_hook; + pause(); delete m_miner; + delete m_hook; } bool Ethash::GPUMiner::report(uint64_t _nonce) { Nonce n = (Nonce)(u64)_nonce; - Result r = EthashAux::eval(m_work.seedHash, m_work.headerHash, n); - if (r.value < m_work.boundary) + Result r = EthashAux::eval(work().seedHash, work().headerHash, n); + if (r.value < work().boundary) return submitProof(Solution{n, r.mixHash}); return false; } -void Ethash::GPUMiner::kickOff(WorkPackage const& _work) +void Ethash::GPUMiner::kickOff() { - m_work = _work; + m_hook->reset(); startWorking(); } void Ethash::GPUMiner::workLoop() { // take local copy of work since it may end up being overwritten by kickOff/pause. - WorkPackage w = m_work; + WorkPackage w = work(); if (!m_miner || m_minerSeed != w.seedHash) { m_minerSeed = w.seedHash; @@ -249,7 +256,6 @@ void Ethash::GPUMiner::pause() { m_hook->abort(); stopWorking(); - m_work.headerHash = h256(); } #endif diff --git a/libethcore/Ethash.h b/libethcore/Ethash.h index 8f1ba3eb3..06e6b9e96 100644 --- a/libethcore/Ethash.h +++ b/libethcore/Ethash.h @@ -87,19 +87,16 @@ public: static unsigned instances() { return std::thread::hardware_concurrency(); } protected: - void kickOff(WorkPackage const& _work) override + void kickOff() override { stopWorking(); - m_work = _work; startWorking(); } - void pause() override { stopWorking(); m_work.reset(); } + void pause() override { stopWorking(); } private: void workLoop() override; - - WorkPackage m_work; }; #if ETH_ETHASHCL || !ETH_TRUE @@ -114,7 +111,7 @@ public: static unsigned instances() { return 1; } protected: - void kickOff(WorkPackage const& _work) override; + void kickOff() override; void pause() override; private: @@ -127,7 +124,6 @@ public: ethash_cl_miner* m_miner = nullptr; h256 m_minerSeed; ///< Last seed in m_miner - WorkPackage m_work; ///< Work to be done by GPU, set with kickOff and picked up in workLoop. }; #else using GPUMiner = CPUMiner; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index ea51b0eb5..084cc59ff 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -40,13 +40,13 @@ namespace eth struct MiningProgress { // MiningProgress& operator+=(MiningProgress const& _mp) { hashes += _mp.hashes; ms = std::max(ms, _mp.ms); return *this; } - unsigned hashes = 0; ///< Total number of hashes computed. - unsigned ms = 0; ///< Total number of milliseconds of mining thus far. + uint64_t hashes = 0; ///< Total number of hashes computed. + uint64_t ms = 0; ///< Total number of milliseconds of mining thus far. }; struct MineInfo: public MiningProgress {}; -inline std::ostream& operator<<(std::ostream& _out, MiningProgress const& _p) +inline std::ostream& operator<<(std::ostream& _out, MiningProgress _p) { _out << (_p.hashes * 1000 / _p.ms) << "H/s = " << _p.hashes << " hashes / " << (double(_p.ms) / 1000) << "s"; return _out; @@ -97,17 +97,19 @@ public: void setWork(WorkPackage const& _work = WorkPackage()) { Guard l(x_work); - if (_work.headerHash == m_work.headerHash) - return; - if (_work.headerHash != h256()) - kickOff(_work); - else if (m_work.headerHash == h256() && _work.headerHash != h256()) - pause(); + auto old = m_work; m_work = _work; + if (!!m_work) + { + pause(); + kickOff(); + } + else if (!m_work && !!old) + pause(); m_hashCount = 0; } - unsigned hashCount() { return m_hashCount; } + uint64_t hashCount() { return m_hashCount; } unsigned index() const { return m_index; } @@ -119,7 +121,7 @@ protected: * @brief Begin working on a given work package, discarding any previous work. * @param _work The package for which to find a solution. */ - virtual void kickOff(WorkPackage const& _work) = 0; + virtual void kickOff() = 0; /** * @brief No work left to be done. Pause until told to kickOff(). @@ -138,7 +140,10 @@ protected: if (m_farm) { Guard l(x_work); - return m_farm->submitProof(_s, m_work, this); + if (!m_farm->submitProof(_s, m_work, this)) + return false; + m_work.reset(); + return true; } return true; } @@ -154,7 +159,7 @@ private: Mutex x_work; WorkPackage m_work; - unsigned m_hashCount = 0; + uint64_t m_hashCount = 0; }; } diff --git a/libethereum/Farm.h b/libethereum/Farm.h index c20c27e6d..6b65e37c2 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -50,6 +50,11 @@ public: using Solution = typename PoW::Solution; using Miner = GenericMiner; + ~GenericFarm() + { + stop(); + } + /** * @brief Sets the current mining mission. * @param _bi The block (header) we wish to be mining. @@ -142,7 +147,7 @@ private: for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); - m_work.headerHash = h256(); + m_work.reset(); return true; } return false; From 6067c939cdb867d84f2b355acceb6834814aaf1f Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Apr 2015 22:49:17 +0200 Subject: [PATCH 28/34] Fix CPU mining deadlock bug. --- exp/main.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++- libethcore/Miner.h | 6 ++-- libethereum/Farm.h | 4 ++- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/exp/main.cpp b/exp/main.cpp index 2055c49e1..54b75d0e2 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -25,6 +25,7 @@ #include "libethash-cl/cl.hpp" #endif #include +#include #include #include #include @@ -106,7 +107,7 @@ int main() cnote << "State after transaction: " << s; cnote << before.diff(s); } -#else +#elif 0 int main() { GenericFarm f; @@ -152,5 +153,76 @@ int main() return 0; } +#else + +void mine(State& s, BlockChain const& _bc) +{ + s.commitToMine(_bc); + GenericFarm f; + bool completed = false; + f.onSolutionFound([&](ProofOfWork::Solution sol) + { + return completed = s.completeMine(sol); + }); + f.setWork(s.info()); + f.startCPU(); + while (!completed) + this_thread::sleep_for(chrono::milliseconds(20)); +} + +int main() +{ + cnote << "Testing State..."; + + KeyPair me = sha3("Gav Wood"); + KeyPair myMiner = sha3("Gav's Miner"); +// KeyPair you = sha3("123"); + + Defaults::setDBPath(boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count())); + + OverlayDB stateDB = State::openDB(); + CanonBlockChain bc; + cout << bc; + + State s(stateDB, BaseState::CanonGenesis, myMiner.address()); + cout << s; + + // Sync up - this won't do much until we use the last state. + s.sync(bc); + + cout << s; + + // Mine to get some ether! + mine(s, bc); + + bc.attemptImport(s.blockData(), stateDB); + + cout << bc; + + s.sync(bc); + + cout << s; + + // Inject a transaction to transfer funds from miner to me. + Transaction t(1000, 10000, 30000, me.address(), bytes(), s.transactionsFrom(myMiner.address()), myMiner.secret()); + assert(t.sender() == myMiner.address()); + s.execute(bc.lastHashes(), t); + + cout << s; + + // Mine to get some ether and set in stone. + s.commitToMine(bc); + s.commitToMine(bc); + mine(s, bc); + bc.attemptImport(s.blockData(), stateDB); + + cout << bc; + + s.sync(bc); + + cout << s; + + return 0; +} #endif diff --git a/libethcore/Miner.h b/libethcore/Miner.h index 084cc59ff..a048b0238 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -69,11 +69,11 @@ public: /** * @brief Called from a Miner to note a WorkPackage has a solution. * @param _p The solution. - * @param _wp The WorkPackage that the Solution is for. + * @param _wp The WorkPackage that the Solution is for; this will be reset if the work is accepted. * @param _finder The miner that found it. * @return true iff the solution was good (implying that mining should be . */ - virtual bool submitProof(Solution const& _p, WorkPackage const& _wp, Miner* _finder) = 0; + virtual bool submitProof(Solution const& _p, WorkPackage& io_wp, Miner* _finder) = 0; }; /** @@ -139,10 +139,8 @@ protected: { if (m_farm) { - Guard l(x_work); if (!m_farm->submitProof(_s, m_work, this)) return false; - m_work.reset(); return true; } return true; diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 6b65e37c2..56bbcb9df 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -136,8 +136,9 @@ private: * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ - bool submitProof(Solution const& _s, WorkPackage const& _wp, Miner* _m) override + bool submitProof(Solution const& _s, WorkPackage& _wp, Miner* _m) override { + ReadGuard l(x_work); if (_wp.headerHash != m_work.headerHash) return false; @@ -147,6 +148,7 @@ private: for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); + _wp.reset(); m_work.reset(); return true; } From 192761a4b1ae38390b07cf24deacce69fd9a7184 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 09:39:00 +0200 Subject: [PATCH 29/34] Various threading fixes. --- CMakeLists.txt | 9 +++++++++ alethzero/CMakeLists.txt | 2 +- alethzero/Transact.cpp | 4 ++-- exp/main.cpp | 28 ++++++++++++++++++++++++++-- libdevcore/Worker.cpp | 4 ++-- libethcore/Miner.h | 31 ++++++++++++++++++------------- libethereum/Client.cpp | 34 ++++++++++++++++++++++++++++++---- libethereum/Client.h | 9 ++++++--- libethereum/Farm.h | 33 ++++++++++++--------------------- 9 files changed, 106 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05862d9f1..9c90dec46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,6 +243,15 @@ elseif (BUNDLE STREQUAL "full") set(TOOLS ON) set(TESTS ON) set(FATDB ON) +elseif (BUNDLE STREQUAL "core") + set(SERPENT OFF) + set(SOLIDITY ON) + set(USENPM OFF) + set(GUI ON) + set(NCURSES OFF) + set(TOOLS ON) + set(TESTS OFF) + set(FATDB ON) elseif (BUNDLE STREQUAL "tests") set(SERPENT ${DECENT_PLATFORM}) set(SOLIDITY ON) diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index c81c86222..b60bca425 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -59,7 +59,7 @@ target_link_libraries(${EXECUTABLE} jsqrc) target_link_libraries(${EXECUTABLE} natspec) target_link_libraries(${EXECUTABLE} ${MHD_LIBRARIES}) -if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) +if (SERPENT) target_link_libraries(${EXECUTABLE} serpent) endif() diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index f1f7477fe..1ebdf9e23 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -37,7 +37,7 @@ #include #include #include -#ifndef _MSC_VER +#if ETH_SERPENT #include #include #endif @@ -220,7 +220,7 @@ static tuple, bytes, string> userInputToCode(string const& _user, errors.push_back("Solidity: Uncaught exception"); } } -#ifndef _MSC_VER +#if ETH_SERPENT else if (sourceIsSerpent(_user)) { try diff --git a/exp/main.cpp b/exp/main.cpp index 54b75d0e2..20f287f43 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -153,7 +154,7 @@ int main() return 0; } -#else +#elif 0 void mine(State& s, BlockChain const& _bc) { @@ -169,7 +170,7 @@ void mine(State& s, BlockChain const& _bc) while (!completed) this_thread::sleep_for(chrono::milliseconds(20)); } - +#elif 0 int main() { cnote << "Testing State..."; @@ -224,5 +225,28 @@ int main() return 0; } +#else +int main() +{ + string tempDir = boost::filesystem::temp_directory_path().string() + "/" + toString(chrono::system_clock::now().time_since_epoch().count()); + + KeyPair myMiner = sha3("Gav's Miner"); + + p2p::Host net("Test"); + cdebug << "Path:" << tempDir; + Client c(&net, tempDir); + + c.setAddress(myMiner.address()); + + this_thread::sleep_for(chrono::milliseconds(1000)); + + c.startMining(); + + this_thread::sleep_for(chrono::milliseconds(6000)); + + c.stopMining(); + + return 0; +} #endif diff --git a/libdevcore/Worker.cpp b/libdevcore/Worker.cpp index 65e8efcbb..8c1fbb9c7 100644 --- a/libdevcore/Worker.cpp +++ b/libdevcore/Worker.cpp @@ -29,7 +29,7 @@ using namespace dev; void Worker::startWorking(IfRunning _ir) { - cnote << "startWorking for thread" << m_name; +// cnote << "startWorking for thread" << m_name; Guard l(x_work); if (m_work && m_work->joinable()) @@ -56,7 +56,7 @@ void Worker::startWorking(IfRunning _ir) void Worker::stopWorking() { - cnote << "stopWorking for thread" << m_name; +// cnote << "stopWorking for thread" << m_name; Guard l(x_work); if (!m_work || !m_work->joinable()) return; diff --git a/libethcore/Miner.h b/libethcore/Miner.h index a048b0238..51a0ff6f6 100644 --- a/libethcore/Miner.h +++ b/libethcore/Miner.h @@ -73,11 +73,12 @@ public: * @param _finder The miner that found it. * @return true iff the solution was good (implying that mining should be . */ - virtual bool submitProof(Solution const& _p, WorkPackage& io_wp, Miner* _finder) = 0; + virtual bool submitProof(Solution const& _p, Miner* _finder) = 0; }; /** * @brief A miner - a member and adoptee of the Farm. + * @warning Not threadsafe. It is assumed Farm will synchronise calls to/from this class. */ template class GenericMiner { @@ -96,15 +97,17 @@ public: void setWork(WorkPackage const& _work = WorkPackage()) { - Guard l(x_work); auto old = m_work; - m_work = _work; - if (!!m_work) + { + Guard l(x_work); + m_work = _work; + } + if (!!_work) { pause(); kickOff(); } - else if (!m_work && !!old) + else if (!_work && !!old) pause(); m_hashCount = 0; } @@ -137,16 +140,18 @@ protected: */ bool submitProof(Solution const& _s) { - if (m_farm) + if (!m_farm) + return true; + if (m_farm->submitProof(_s, this)) { - if (!m_farm->submitProof(_s, m_work, this)) - return false; + Guard l(x_work); + m_work.reset(); return true; } - return true; + return false; } - WorkPackage const& work() const { return m_work; } + WorkPackage const& work() const { Guard l(x_work); return m_work; } void accumulateHashes(unsigned _n) { m_hashCount += _n; } @@ -154,10 +159,10 @@ private: FarmFace* m_farm = nullptr; unsigned m_index; - Mutex x_work; - WorkPackage m_work; - uint64_t m_hashCount = 0; + + WorkPackage m_work; + mutable Mutex x_work; }; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 79216f609..46da3e1f7 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -192,6 +192,27 @@ bool Client::isSyncing() const return false; } +void Client::startedWorking() +{ + // Synchronise the state according to the head of the block chain. + // TODO: currently it contains keys for *all* blocks. Make it remove old ones. + cdebug << "startedWorking()"; + WriteGuard l(x_stateDB); + + cdebug << m_bc.number() << m_bc.currentHash(); + + cdebug << "Pre:" << m_preMine.info(); + cdebug << "Post:" << m_postMine.info(); + cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); + + m_preMine.sync(m_bc); + m_postMine = m_preMine; + + cdebug << "Pre:" << m_preMine.info(); + cdebug << "Post:" << m_postMine.info(); + cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); +} + void Client::doneWorking() { // Synchronise the state according to the head of the block chain. @@ -403,10 +424,11 @@ bool Client::submitWork(ProofOfWork::Solution const& _solution) return false; newBlock = m_postMine.blockData(); } - + m_bq.import(&newBlock, m_bc); +/* ImportRoute ir = m_bc.attemptImport(newBlock, m_stateDB); if (!ir.first.empty()) - onChainChanged(ir); + onChainChanged(ir);*/ return true; } @@ -500,9 +522,9 @@ void Client::onChainChanged(ImportRoute const& _ir) m_postMine = m_preMine; changeds.insert(PendingChangedFilter); - x_stateDB.unlock(); + x_stateDB.unlock_shared(); onPostStateChanged(); - x_stateDB.lock(); + x_stateDB.lock_shared(); } } @@ -514,8 +536,12 @@ void Client::onPostStateChanged() cnote << "Post state changed: Restarting mining..."; { WriteGuard l(x_stateDB); + cdebug << "Pre:" << m_preMine.info(); m_postMine.commitToMine(m_bc); m_miningInfo = m_postMine.info(); + cdebug << "Pre:" << m_preMine.info(); + cdebug << "Post:" << m_postMine.info(); + cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); } m_farm.setWork(m_miningInfo); diff --git a/libethereum/Client.h b/libethereum/Client.h index 2235c4459..16033a11f 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -169,7 +169,7 @@ public: /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); } + void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); onPostStateChanged(); } /// Stop mining. /// NOT thread-safe void stopMining() override { m_farm.stop(); } @@ -229,11 +229,14 @@ protected: void noteChanged(h256Set const& _filters); private: + /// Called when Worker is starting. + void startedWorking() override; + /// Do some work. Handles blockchain maintenance and mining. - virtual void doWork(); + void doWork() override; /// Called when Worker is exiting. - virtual void doneWorking(); + void doneWorking() override; /// Magically called when the chain has changed. An import route is provided. /// Called by either submitWork() or in our main thread through syncBlockQueue(). diff --git a/libethereum/Farm.h b/libethereum/Farm.h index 56bbcb9df..afca853ed 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -61,8 +61,7 @@ public: */ void setWork(BlockInfo const& _bi) { - WriteGuard l(x_work); - ReadGuard l2(x_miners); + WriteGuard l(x_minerWork); m_header = _bi; auto p = PoW::package(m_header); if (p.headerHash == m_work.headerHash) @@ -90,14 +89,14 @@ public: */ void stop() { - WriteGuard l(x_miners); + WriteGuard l(x_minerWork); m_miners.clear(); + m_work.reset(); } bool isMining() const { - ReadGuard l(x_miners); - return !m_miners.empty(); + return !!m_work; } /** @@ -109,7 +108,7 @@ public: MiningProgress p; p.ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_lastStart).count(); { - ReadGuard l2(x_miners); + ReadGuard l2(x_minerWork); for (auto const& i: m_miners) p.hashes += i->hashCount(); } @@ -127,7 +126,7 @@ public: */ void onSolutionFound(SolutionFound const& _handler) { m_onSolutionFound = _handler; } - WorkPackage work() const { ReadGuard l(x_work); return m_work; } + WorkPackage work() const { ReadGuard l(x_minerWork); return m_work; } private: /** @@ -136,19 +135,14 @@ private: * @param _wp The WorkPackage that the Solution is for. * @return true iff the solution was good (implying that mining should be . */ - bool submitProof(Solution const& _s, WorkPackage& _wp, Miner* _m) override + bool submitProof(Solution const& _s, Miner* _m) override { - ReadGuard l(x_work); - if (_wp.headerHash != m_work.headerHash) - return false; - if (m_onSolutionFound && m_onSolutionFound(_s)) { - ReadGuard l(x_miners); + WriteGuard ul(x_minerWork); for (std::shared_ptr const& m: m_miners) if (m.get() != _m) m->setWork(); - _wp.reset(); m_work.reset(); return true; } @@ -161,8 +155,7 @@ private: template bool start() { - ReadGuard l(x_work); - WriteGuard l2(x_miners); + WriteGuard l(x_minerWork); if (!m_miners.empty() && !!std::dynamic_pointer_cast(m_miners[0])) return true; m_miners.clear(); @@ -181,17 +174,15 @@ private: m_lastStart = std::chrono::steady_clock::now(); } - mutable SharedMutex x_miners; + mutable SharedMutex x_minerWork; std::vector> m_miners; + WorkPackage m_work; + BlockInfo m_header; mutable SharedMutex x_progress; mutable MiningProgress m_progress; std::chrono::steady_clock::time_point m_lastStart; - mutable SharedMutex x_work; - WorkPackage m_work; - BlockInfo m_header; - SolutionFound m_onSolutionFound; }; From 45bacf856602f156ebe9a5fd3ceeff330b765216 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 10:18:34 +0200 Subject: [PATCH 30/34] AlethZero fixes. --- libethcore/BlockInfo.cpp | 9 +++++++-- libethcore/BlockInfo.h | 1 + libethcore/EthashAux.cpp | 29 ++++++++++++++++++++++++++--- libethcore/EthashAux.h | 4 +++- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/libethcore/BlockInfo.cpp b/libethcore/BlockInfo.cpp index 6cd431931..b45bdc57e 100644 --- a/libethcore/BlockInfo.cpp +++ b/libethcore/BlockInfo.cpp @@ -23,6 +23,7 @@ #include #include #include +#include "EthashAux.h" #include "ProofOfWork.h" #include "Exceptions.h" #include "Params.h" @@ -63,8 +64,7 @@ void BlockInfo::clear() h256 const& BlockInfo::seedHash() const { if (!m_seedHash) - for (u256 n = number; n >= c_epochDuration; n -= c_epochDuration) - m_seedHash = sha3(m_seedHash); + m_seedHash = EthashAux::seedHash((unsigned)number); return m_seedHash; } @@ -145,9 +145,14 @@ void BlockInfo::populateFromHeader(RLP const& _header, Strictness _s, h256 const throw; } + if (number > ~(unsigned)0) + throw InvalidNumber(); + // check it hashes according to proof of work or that it's the genesis block. if (_s == CheckEverything && parentHash && !ProofOfWork::verify(*this)) BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty)); + else if (_s == QuickNonce && parentHash && !ProofOfWork::preVerify(*this)) + BOOST_THROW_EXCEPTION(InvalidBlockNonce() << errinfo_hash256(headerHash(WithoutNonce)) << errinfo_nonce(nonce) << errinfo_difficulty(difficulty)); if (_s != CheckNothing) { diff --git a/libethcore/BlockInfo.h b/libethcore/BlockInfo.h index dffff73f4..79c12ebb4 100644 --- a/libethcore/BlockInfo.h +++ b/libethcore/BlockInfo.h @@ -39,6 +39,7 @@ enum IncludeNonce enum Strictness { CheckEverything, + QuickNonce, IgnoreNonce, CheckNothing }; diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index 969310dac..fb4c2820d 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -63,24 +63,47 @@ ethash_params EthashAux::params(unsigned _n) return p; } +h256 EthashAux::seedHash(unsigned _number) +{ + unsigned epoch = _number / ETHASH_EPOCH_LENGTH; + RecursiveGuard l(get()->x_this); + if (_number < get()->m_seedHashes.size()) + return get()->m_seedHashes[_number]; + h256 ret; + unsigned n = 0; + if (!get()->m_seedHashes.empty()) + { + ret = get()->m_seedHashes.back(); + n = get()->m_seedHashes.size() - 1; + } + cdebug << "Searching for seedHash of epoch " << epoch; + for (; n < epoch; ++n, ret = sha3(ret)) + cdebug << "Epoch" << n << "is" << ret.abridged(); + return ret; +} + ethash_params EthashAux::params(h256 const& _seedHash) { RecursiveGuard l(get()->x_this); unsigned epoch = 0; try { - epoch = get()->m_seedHashes.at(_seedHash); + epoch = get()->m_epochs.at(_seedHash); } catch (...) { - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = h256(h)) {} + cdebug << "Searching for seedHash " << _seedHash.abridged(); + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h)) + { + cdebug << "Epoch" << epoch << "is" << h.abridged(); + } if (epoch == 2048) { std::ostringstream error; error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } - get()->m_seedHashes[_seedHash] = epoch; + get()->m_epochs[_seedHash] = epoch; } return params(epoch * ETHASH_EPOCH_LENGTH); } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index aec1089a2..94c2243e0 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -36,6 +36,7 @@ public: using LightType = void const*; using FullType = void const*; + static h256 seedHash(unsigned _number); static ethash_params params(BlockInfo const& _header); static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); @@ -58,7 +59,8 @@ private: std::map m_lights; std::map m_fulls; - std::map m_seedHashes; + std::map m_epochs; + h256s m_seedHashes; }; } From 76518d519f8559860d2db0618a8e7ae4d5b5d080 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 10:37:11 +0200 Subject: [PATCH 31/34] Marginally better Client async code. --- libethereum/Client.cpp | 14 ++++---------- libethereum/Client.h | 9 ++++----- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 46da3e1f7..cc579ec08 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -571,19 +571,13 @@ void Client::doWork() { // TODO: Use condition variable rather than this rubbish. - Guard l(x_fakeSignalSystemState); - - if (m_syncTransactionQueue) - { - m_syncTransactionQueue = false; + bool t = true; + if (m_syncTransactionQueue.compare_exchange_strong(t, false)) syncTransactionQueue(); - } - if (m_syncBlockQueue) - { - m_syncBlockQueue = false; + t = true; + if (m_syncBlockQueue.compare_exchange_strong(t, false)) syncBlockQueue(); - } checkWatchGarbage(); diff --git a/libethereum/Client.h b/libethereum/Client.h index 16033a11f..289df3fe0 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -249,10 +249,10 @@ private: void syncTransactionQueue(); /// Magically called when m_tq needs syncing. Be nice and don't block. - void onTransactionQueueReady() { Guard l(x_fakeSignalSystemState); m_syncTransactionQueue = true; } + void onTransactionQueueReady() { m_syncTransactionQueue = true; } /// Magically called when m_tq needs syncing. Be nice and don't block. - void onBlockQueueReady() { Guard l(x_fakeSignalSystemState); m_syncBlockQueue = true; } + void onBlockQueueReady() { m_syncBlockQueue = true; } /// Called when the post state has changed (i.e. when more transactions are in it or we're mining on a new block). /// This updates m_miningInfo. @@ -286,9 +286,8 @@ private: ///< When did we last both doing GC on the watches? // TODO!!!!!! REPLACE WITH A PROPER X-THREAD ASIO SIGNAL SYSTEM (could just be condition variables) - mutable Mutex x_fakeSignalSystemState; - bool m_syncTransactionQueue = false; - bool m_syncBlockQueue = false; + std::atomic m_syncTransactionQueue = {false}; + std::atomic m_syncBlockQueue = {false}; }; } From 8f532e81cbfcea2f746297592e963108319e0c52 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 12:43:00 +0200 Subject: [PATCH 32/34] Fix to ethash seedHash caching. --- alethzero/Main.ui | 6 ++++ alethzero/MainWin.cpp | 5 +++ alethzero/MainWin.h | 1 + eth/main.cpp | 41 +++++++++++++++++++++- libdevcore/Guards.h | 71 ++++++++++++++++++++++++++++++++++++++ libethcore/EthashAux.cpp | 33 +++++++++--------- libethcore/EthashAux.h | 2 +- libethcore/Params.cpp | 1 - libethcore/Params.h | 1 - libethereum/BlockQueue.cpp | 12 +++++++ libethereum/BlockQueue.h | 3 ++ libethereum/Client.cpp | 47 ++++++++++++++----------- libethereum/Client.h | 4 ++- libethereum/Farm.h | 6 +++- 14 files changed, 190 insertions(+), 43 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index 3f3d1e237..8e48793c9 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -180,6 +180,7 @@ + @@ -1679,6 +1680,11 @@ font-size: 14pt &GPU Mining + + + Retry Unknown Parent Blocks + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 2e4478594..a60875ba6 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -1752,6 +1752,11 @@ void Main::on_clearPending_triggered() refreshAll(); } +void Main::on_retryUnknown_triggered() +{ + ethereum()->retryUnkonwn(); +} + void Main::on_killBlockchain_triggered() { writeSettings(); diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index a5c74eeaa..a8579ed01 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -163,6 +163,7 @@ private slots: void on_usePrivate_triggered(); void on_turboMining_triggered(); void on_jitvm_triggered(); + void on_retryUnknown_triggered(); // Debugger void on_debugCurrent_triggered(); diff --git a/eth/main.cpp b/eth/main.cpp index 24bf839aa..461494cef 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -32,11 +32,12 @@ #include #include #include +#include +#include #include #include #include #include -#include #if ETH_READLINE #include #include @@ -111,6 +112,7 @@ void help() << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -B,--block-fees Set the block fee profit in the reference unit e.g. ยข (Default: 15)." << endl << " -c,--client-name Add a name to your client's version string (default: blank)." << endl + << " -C,--check-pow Check PoW credentials for validity." << endl << " -d,--db-path Load database from path (default: ~/.ethereum " << endl << " /Etherum or Library/Application Support/Ethereum)." << endl << " -D,--create-dag Create the DAG in preparation for mining on given block and exit." << endl @@ -401,6 +403,43 @@ int main(int argc, char** argv) return -1; } } + else if ((arg == "-C" || arg == "--check-pow") && i + 4 < argc) + { + string m; + try + { + BlockInfo bi; + m = boost::to_lower_copy(string(argv[++i])); + h256 powHash(m); + m = boost::to_lower_copy(string(argv[++i])); + h256 seedHash; + if (m.size() == 64 || m.size() == 66) + seedHash = h256(m); + else + seedHash = EthashAux::seedHash(stol(m)); + m = boost::to_lower_copy(string(argv[++i])); + bi.difficulty = u256(m); + auto boundary = bi.boundary(); + m = boost::to_lower_copy(string(argv[++i])); + bi.nonce = h64(m); + auto r = EthashAux::eval(seedHash, powHash, bi.nonce); + bool valid = r.value < boundary; + cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; + cout << r.value << (valid ? " < " : " >= ") << boundary << endl; + cout << " where " << boundary << " = 2^256 / " << bi.difficulty << endl; + cout << " and " << r.value << " = ethash(" << powHash << ", " << bi.nonce << ")" << endl; + cout << " with seed as " << seedHash << endl; + if (valid) + cout << "(mixHash = " << r.mixHash << ")" << endl; + cout << "SHA3( light(seed) ) = " << sha3(bytesConstRef((byte const*)EthashAux::light(seedHash), EthashAux::params(seedHash).cache_size)) << endl; + exit(0); + } + catch (...) + { + cerr << "Bad " << arg << " option: " << m << endl; + return -1; + } + } else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) { try diff --git a/libdevcore/Guards.h b/libdevcore/Guards.h index f5c64b041..4229428ce 100644 --- a/libdevcore/Guards.h +++ b/libdevcore/Guards.h @@ -38,4 +38,75 @@ using UpgradableGuard = boost::upgrade_lock; using UpgradeGuard = boost::upgrade_to_unique_lock; using WriteGuard = boost::unique_lock; +template +struct GenericGuardBool: GuardType +{ + GenericGuardBool(MutexType& _m): GuardType(_m) {} + bool b = true; +}; +template +struct GenericUnguardBool +{ + GenericUnguardBool(MutexType& _m): m(_m) { m.unlock(); } + ~GenericUnguardBool() { m.lock(); } + bool b = true; + MutexType& m; +}; +template +struct GenericUnguardSharedBool +{ + GenericUnguardSharedBool(MutexType& _m): m(_m) { m.unlock_shared(); } + ~GenericUnguardSharedBool() { m.lock_shared(); } + bool b = true; + MutexType& m; +}; + +/** @brief Simple block guard. + * The expression/block following is guarded though the given mutex. + * Usage: + * @code + * Mutex m; + * unsigned d; + * ... + * ETH_GUARDED(m) d = 1; + * ... + * ETH_GUARDED(m) { for (auto d = 10; d > 0; --d) foo(d); d = 0; } + * @endcode + * + * There are several variants of this basic mechanism for different Mutex types and Guards. + * + * There is also the UNGUARD variant which allows an unguarded expression/block to exist within a + * guarded expression. eg: + * + * @code + * Mutex m; + * int d; + * ... + * ETH_GUARDED(m) + * { + * for (auto d = 50; d > 25; --d) + * foo(d); + * ETH_UNGUARDED(m) + * bar(); + * for (; d > 0; --d) + * foo(d); + * } + * @endcode + */ + +#define ETH_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_READ_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_WRITE_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_RECURSIVE_GUARDED(MUTEX) \ + for (GenericGuardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_UNGUARDED(MUTEX) \ + for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_READ_UNGUARDED(MUTEX) \ + for (GenericUnguardSharedBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) +#define ETH_WRITE_UNGUARDED(MUTEX) \ + for (GenericUnguardBool __eth_l(MUTEX); __eth_l.b; __eth_l.b = false) + } diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index fb4c2820d..061af566e 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -67,19 +67,24 @@ h256 EthashAux::seedHash(unsigned _number) { unsigned epoch = _number / ETHASH_EPOCH_LENGTH; RecursiveGuard l(get()->x_this); - if (_number < get()->m_seedHashes.size()) - return get()->m_seedHashes[_number]; - h256 ret; - unsigned n = 0; - if (!get()->m_seedHashes.empty()) + if (epoch >= get()->m_seedHashes.size()) { - ret = get()->m_seedHashes.back(); - n = get()->m_seedHashes.size() - 1; + h256 ret; + unsigned n = 0; + if (!get()->m_seedHashes.empty()) + { + ret = get()->m_seedHashes.back(); + n = get()->m_seedHashes.size() - 1; + } + get()->m_seedHashes.resize(epoch + 1); + cdebug << "Searching for seedHash of epoch " << epoch; + for (; n <= epoch; ++n, ret = sha3(ret)) + { + get()->m_seedHashes[n] = ret; + cdebug << "Epoch" << n << "is" << ret.abridged(); + } } - cdebug << "Searching for seedHash of epoch " << epoch; - for (; n < epoch; ++n, ret = sha3(ret)) - cdebug << "Epoch" << n << "is" << ret.abridged(); - return ret; + return get()->m_seedHashes[epoch]; } ethash_params EthashAux::params(h256 const& _seedHash) @@ -93,17 +98,13 @@ ethash_params EthashAux::params(h256 const& _seedHash) catch (...) { cdebug << "Searching for seedHash " << _seedHash.abridged(); - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h)) - { - cdebug << "Epoch" << epoch << "is" << h.abridged(); - } + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} if (epoch == 2048) { std::ostringstream error; error << "apparent block number for " << _seedHash.abridged() << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); } - get()->m_epochs[_seedHash] = epoch; } return params(epoch * ETHASH_EPOCH_LENGTH); } diff --git a/libethcore/EthashAux.h b/libethcore/EthashAux.h index 94c2243e0..c927a012b 100644 --- a/libethcore/EthashAux.h +++ b/libethcore/EthashAux.h @@ -41,7 +41,7 @@ public: static ethash_params params(h256 const& _seedHash); static ethash_params params(unsigned _n); static LightType light(BlockInfo const& _header); - static LightType light(h256 const& _header); + static LightType light(h256 const& _seedHash); static bytesConstRef full(BlockInfo const& _header, bytesRef _dest = bytesRef()); static bytesConstRef full(h256 const& _header, bytesRef _dest = bytesRef()); diff --git a/libethcore/Params.cpp b/libethcore/Params.cpp index 029f8b47a..655c8a78b 100644 --- a/libethcore/Params.cpp +++ b/libethcore/Params.cpp @@ -30,7 +30,6 @@ namespace eth //--- BEGIN: AUTOGENERATED FROM github.com/ethereum/common/params.json u256 const c_genesisDifficulty = 131072; u256 const c_maximumExtraDataSize = 1024; -u256 const c_epochDuration = 30000; u256 const c_genesisGasLimit = 3141592; u256 const c_minGasLimit = 125000; u256 const c_gasLimitBoundDivisor = 1024; diff --git a/libethcore/Params.h b/libethcore/Params.h index 46b30e2c3..b957f9737 100644 --- a/libethcore/Params.h +++ b/libethcore/Params.h @@ -37,7 +37,6 @@ extern u256 const c_minimumDifficulty; extern u256 const c_difficultyBoundDivisor; extern u256 const c_durationLimit; extern u256 const c_maximumExtraDataSize; -extern u256 const c_epochDuration; extern u256 const c_stackLimit; extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them. diff --git a/libethereum/BlockQueue.cpp b/libethereum/BlockQueue.cpp index e9dd99cd1..b76e4bed6 100644 --- a/libethereum/BlockQueue.cpp +++ b/libethereum/BlockQueue.cpp @@ -183,3 +183,15 @@ void BlockQueue::noteReadyWithoutWriteGuard(h256 _good) m_unknown.erase(r.first, r.second); } } + +void BlockQueue::retryAllUnknown() +{ + for (auto it = m_unknown.begin(); it != m_unknown.end(); ++it) + { + m_ready.push_back(it->second.second); + auto newReady = it->second.first; + m_unknownSet.erase(newReady); + m_readySet.insert(newReady); + } + m_unknown.clear(); +} diff --git a/libethereum/BlockQueue.h b/libethereum/BlockQueue.h index f5cdf7ab5..4a503d114 100644 --- a/libethereum/BlockQueue.h +++ b/libethereum/BlockQueue.h @@ -71,6 +71,9 @@ public: /// Notify the queue that the chain has changed and a new block has attained 'ready' status (i.e. is in the chain). void noteReady(h256 _b) { WriteGuard l(m_lock); noteReadyWithoutWriteGuard(_b); } + /// Force a retry of all the blocks with unknown parents. + void retryAllUnknown(); + /// Get information on the items queued. std::pair items() const { ReadGuard l(m_lock); return std::make_pair(m_ready.size(), m_unknown.size()); } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index cc579ec08..ea8efd310 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -436,15 +436,12 @@ void Client::syncBlockQueue() { ImportRoute ir; + cwork << "BQ ==> CHAIN ==> STATE"; { WriteGuard l(x_stateDB); - cwork << "BQ ==> CHAIN ==> STATE"; OverlayDB db = m_stateDB; - x_stateDB.unlock(); - - tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); - - x_stateDB.lock(); + ETH_WRITE_UNGUARDED(x_stateDB) + tie(ir.first, ir.second, m_syncBlockQueue) = m_bc.sync(m_bq, db, 100); if (ir.first.empty()) return; m_stateDB = db; @@ -458,7 +455,11 @@ void Client::syncTransactionQueue() cwork << "postSTATE <== TQ"; h256Set changeds; - TransactionReceipts newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + TransactionReceipts newPendingReceipts; + + ETH_WRITE_GUARDED(x_stateDB) + newPendingReceipts = m_postMine.sync(m_bc, m_tq, *m_gp); + if (newPendingReceipts.size()) { for (size_t i = 0; i < newPendingReceipts.size(); i++) @@ -512,8 +513,7 @@ void Client::onChainChanged(ImportRoute const& _ir) // RESTART MINING // LOCKS REALLY NEEDED? - { - ReadGuard l(x_stateDB); + ETH_WRITE_GUARDED(x_stateDB) if (m_preMine.sync(m_bc) || m_postMine.address() != m_preMine.address()) { if (isMining()) @@ -522,11 +522,9 @@ void Client::onChainChanged(ImportRoute const& _ir) m_postMine = m_preMine; changeds.insert(PendingChangedFilter); - x_stateDB.unlock_shared(); - onPostStateChanged(); - x_stateDB.lock_shared(); + ETH_WRITE_UNGUARDED(x_stateDB) + onPostStateChanged(); } - } noteChanged(changeds); } @@ -534,17 +532,24 @@ void Client::onChainChanged(ImportRoute const& _ir) void Client::onPostStateChanged() { cnote << "Post state changed: Restarting mining..."; + if (isMining()) { - WriteGuard l(x_stateDB); - cdebug << "Pre:" << m_preMine.info(); - m_postMine.commitToMine(m_bc); - m_miningInfo = m_postMine.info(); - cdebug << "Pre:" << m_preMine.info(); - cdebug << "Post:" << m_postMine.info(); - cdebug << "Pre:" << m_preMine.info().headerHash(WithoutNonce) << "; Post:" << m_postMine.info().headerHash(WithoutNonce); + { + WriteGuard l(x_stateDB); + m_postMine.commitToMine(m_bc); + m_miningInfo = m_postMine.info(); + } + m_farm.setWork(m_miningInfo); } +} - m_farm.setWork(m_miningInfo); +void Client::startMining() +{ + if (m_turboMining) + m_farm.startGPU(); + else + m_farm.startCPU(); + onPostStateChanged(); } void Client::noteChanged(h256Set const& _filters) diff --git a/libethereum/Client.h b/libethereum/Client.h index 289df3fe0..dedb3bcf1 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -169,7 +169,7 @@ public: /// Start mining. /// NOT thread-safe - call it & stopMining only from a single thread - void startMining() override { if (m_turboMining) m_farm.startGPU(); else m_farm.startCPU(); onPostStateChanged(); } + void startMining() override; /// Stop mining. /// NOT thread-safe void stopMining() override { m_farm.stop(); } @@ -202,6 +202,8 @@ public: void clearPending(); /// Kills the blockchain. Just for debug use. void killChain(); + /// Retries all blocks with unknown parents. + void retryUnkonwn() { m_bq.retryAllUnknown(); } protected: /// InterfaceStub methods diff --git a/libethereum/Farm.h b/libethereum/Farm.h index afca853ed..26d4b139e 100644 --- a/libethereum/Farm.h +++ b/libethereum/Farm.h @@ -92,11 +92,12 @@ public: WriteGuard l(x_minerWork); m_miners.clear(); m_work.reset(); + m_isMining = false; } bool isMining() const { - return !!m_work; + return m_isMining; } /** @@ -165,6 +166,7 @@ private: m_miners.push_back(std::shared_ptr(new MinerType(std::make_pair(this, i)))); m_miners.back()->setWork(m_work); } + m_isMining = true; resetTimer(); return true; } @@ -179,6 +181,8 @@ private: WorkPackage m_work; BlockInfo m_header; + std::atomic m_isMining = {false}; + mutable SharedMutex x_progress; mutable MiningProgress m_progress; std::chrono::steady_clock::time_point m_lastStart; From 036214820080b41eac8ded1657b5983bffa18fba Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 12:54:03 +0200 Subject: [PATCH 33/34] Version bump. Minor fix. --- libdevcore/Common.cpp | 2 +- libethereum/Client.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libdevcore/Common.cpp b/libdevcore/Common.cpp index b6e8e7f93..78b3d9c30 100644 --- a/libdevcore/Common.cpp +++ b/libdevcore/Common.cpp @@ -27,7 +27,7 @@ using namespace dev; namespace dev { -char const* Version = "0.9.7"; +char const* Version = "0.9.8"; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index ea8efd310..4f9af91a9 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -332,7 +332,8 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed) void Client::setForceMining(bool _enable) { m_forceMining = _enable; - startMining(); + if (isMining()) + startMining(); } MiningProgress Client::miningProgress() const From 609d650182f177deaf09e311c725eb7f40c6f1c5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 14 Apr 2015 13:30:10 +0200 Subject: [PATCH 34/34] Windows warning fixes. Mac build fixes. --- eth/main.cpp | 2 +- libdevcore/CommonData.h | 4 ++-- libethash/ethash.h | 4 ++-- libethash/io.c | 6 +++--- libethcore/Ethash.cpp | 4 ++-- mix/ContractCallDataEncoder.cpp | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index 461494cef..db6acbbca 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -364,7 +364,7 @@ int main(int argc, char** argv) { coinbase = h160(fromHex(argv[++i], WhenError::Throw)); } - catch (BadHexCharacter& _e) + catch (BadHexCharacter&) { cerr << "Bad hex in " << arg << " option: " << argv[i] << endl; return -1; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 38ccd71f0..93bad71a3 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -116,9 +116,9 @@ inline void toBigEndian(_T _val, _Out& o_out) template inline _T fromBigEndian(_In const& _bytes) { - _T ret = 0; + _T ret = (_T)0; for (auto i: _bytes) - ret = (ret << 8) | (byte)(typename std::make_unsigned::type)i; + ret = (_T)((ret << 8) | (byte)(typename std::make_unsigned::type)i); return ret; } diff --git a/libethash/ethash.h b/libethash/ethash.h index 7594fc835..bc62a29cc 100644 --- a/libethash/ethash.h +++ b/libethash/ethash.h @@ -85,7 +85,7 @@ typedef uint8_t const ethash_seedhash_t[32]; typedef void const* ethash_light_t; static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) { - void* ret = malloc(params->cache_size); + void* ret = malloc((size_t)params->cache_size); ethash_mkcache(ret, params, seed); return ret; } @@ -98,7 +98,7 @@ static inline void ethash_delete_light(ethash_light_t light) { typedef void const* ethash_full_t; static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) { - void* ret = malloc(params->full_size); + void* ret = malloc((size_t)params->full_size); ethash_compute_full_data(ret, params, light); return ret; } diff --git a/libethash/io.c b/libethash/io.c index 0e935fa59..07387caaf 100644 --- a/libethash/io.c +++ b/libethash/io.c @@ -61,13 +61,13 @@ bool ethash_io_write(char const *dirname, { char info_buffer[DAG_MEMO_BYTESIZE]; // allocate the bytes - uint8_t *temp_data_ptr = malloc(params->full_size); + uint8_t *temp_data_ptr = malloc((size_t)params->full_size); if (!temp_data_ptr) { goto end; } ethash_compute_full_data(temp_data_ptr, params, cache); - if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, params->full_size)) { + if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, (size_t)params->full_size)) { goto fail_free; } @@ -77,7 +77,7 @@ bool ethash_io_write(char const *dirname, } *data = temp_data_ptr; - *data_size = params->full_size; + *data_size = (size_t)params->full_size; return true; fail_free: diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 3dd9d3b60..a777205e9 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -48,7 +48,7 @@ namespace dev namespace eth { -const Ethash::WorkPackage Ethash::NullWorkPackage; +const Ethash::WorkPackage Ethash::NullWorkPackage = Ethash::WorkPackage(); std::string Ethash::name() { @@ -81,7 +81,7 @@ bool Ethash::preVerify(BlockInfo const& _header) h256 boundary = u256((bigint(1) << 256) / _header.difficulty); - return ethash_quick_check_difficulty( + return !!ethash_quick_check_difficulty( _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_header.nonce, _header.mixHash.data(), diff --git a/mix/ContractCallDataEncoder.cpp b/mix/ContractCallDataEncoder.cpp index 56aeb1d34..e1f02f631 100644 --- a/mix/ContractCallDataEncoder.cpp +++ b/mix/ContractCallDataEncoder.cpp @@ -118,7 +118,7 @@ unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, Solidit result = bytes(alignSize); toBigEndian((u256)i, result); } - catch (std::exception const& ex) + catch (std::exception const&) { // manage input as a string. QByteArray bytesAr = src.toLocal8Bit();