diff --git a/src/test/radix_tests.cpp b/src/test/radix_tests.cpp index b6c293151..ead1d0498 100644 --- a/src/test/radix_tests.cpp +++ b/src/test/radix_tests.cpp @@ -1,380 +1,371 @@ // Copyright (c) 2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include "test/lcg.h" #include #include #include #include BOOST_FIXTURE_TEST_SUITE(radix_tests, BasicTestingSetup) -/** - * Version of Boost::test prior to 1.64 have issues when dealing with nullptr_t. - * In order to work around this, we ensure that the null pointers are typed in a - * way that Boost will like better. - * - * TODO: Use nullptr directly once the minimum version of boost is 1.64 or more. - */ -#define NULLPTR(T) static_cast(nullptr) - template struct TestElement { K key; TestElement(K keyIn) : key(keyIn) {} const K &getId() const { return key; } IMPLEMENT_RCU_REFCOUNT(uint32_t); }; template void testInsert() { typedef TestElement E; RadixTree mytree; auto zero = RCUPtr::make(0); auto one = RCUPtr::make(1); auto two = RCUPtr::make(2); auto three = RCUPtr::make(3); // Inserting a new element into the tree returns true. BOOST_CHECK(mytree.insert(one)); // Inserting an element already in the tree returns false. BOOST_CHECK(!mytree.insert(one)); // Let's insert more elements and check behavior stays consistent. BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(!mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(!mytree.insert(two)); BOOST_CHECK(!mytree.insert(three)); // Check extreme values. typedef typename std::make_signed::type SK; auto maxsigned = RCUPtr::make(std::numeric_limits::max()); auto minsigned = RCUPtr::make(std::numeric_limits::min()); typedef typename std::make_unsigned::type UK; auto minusone = RCUPtr::make(std::numeric_limits::max()); auto minustwo = RCUPtr::make(std::numeric_limits::max() - 1); // Insert them into the tree. BOOST_CHECK(mytree.insert(maxsigned)); BOOST_CHECK(mytree.insert(minsigned)); BOOST_CHECK(mytree.insert(minusone)); BOOST_CHECK(mytree.insert(minustwo)); // All elements are now in the tree. BOOST_CHECK(!mytree.insert(zero)); BOOST_CHECK(!mytree.insert(one)); BOOST_CHECK(!mytree.insert(two)); BOOST_CHECK(!mytree.insert(three)); BOOST_CHECK(!mytree.insert(maxsigned)); BOOST_CHECK(!mytree.insert(minsigned)); BOOST_CHECK(!mytree.insert(minusone)); BOOST_CHECK(!mytree.insert(minustwo)); } BOOST_AUTO_TEST_CASE(insert_test) { testInsert(); testInsert(); testInsert(); testInsert(); } template void testGet() { typedef TestElement E; RadixTree mytree; auto zero = RCUPtr::make(0); auto one = RCUPtr::make(1); auto two = RCUPtr::make(2); auto three = RCUPtr::make(3); // There are no elements in the tree so far. BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); // Insert an element into the tree and check it is there. BOOST_CHECK(mytree.insert(one)); BOOST_CHECK_EQUAL(mytree.get(1), one); // Let's insert more elements and check they are recovered properly. BOOST_CHECK_EQUAL(mytree.get(0), NULLPTR(E)); BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK_EQUAL(mytree.get(0), zero); BOOST_CHECK_EQUAL(mytree.get(1), one); // More elements. BOOST_CHECK_EQUAL(mytree.get(2), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(3), NULLPTR(E)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK_EQUAL(mytree.get(0), zero); BOOST_CHECK_EQUAL(mytree.get(1), one); BOOST_CHECK_EQUAL(mytree.get(2), two); BOOST_CHECK_EQUAL(mytree.get(3), three); // Check extreme values. typedef typename std::make_signed::type SK; K maxsk = std::numeric_limits::max(); K minsk = std::numeric_limits::min(); typedef typename std::make_unsigned::type UK; K maxuk = std::numeric_limits::max(); auto maxsigned = RCUPtr::make(maxsk); auto minsigned = RCUPtr::make(minsk); auto minusone = RCUPtr::make(maxuk); auto minustwo = RCUPtr::make(maxuk - 1); // Check that they are not in the tree. BOOST_CHECK_EQUAL(mytree.get(maxsk), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(minsk), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(maxuk), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), NULLPTR(E)); // Insert into the tree. BOOST_CHECK(mytree.insert(maxsigned)); BOOST_CHECK(mytree.insert(minsigned)); BOOST_CHECK(mytree.insert(minusone)); BOOST_CHECK(mytree.insert(minustwo)); // And now they all are in the tree. BOOST_CHECK_EQUAL(mytree.get(0), zero); BOOST_CHECK_EQUAL(mytree.get(1), one); BOOST_CHECK_EQUAL(mytree.get(2), two); BOOST_CHECK_EQUAL(mytree.get(3), three); BOOST_CHECK_EQUAL(mytree.get(maxsk), maxsigned); BOOST_CHECK_EQUAL(mytree.get(minsk), minsigned); BOOST_CHECK_EQUAL(mytree.get(maxuk), minusone); BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), minustwo); } BOOST_AUTO_TEST_CASE(get_test) { testGet(); testGet(); testGet(); testGet(); } template void testRemove() { typedef TestElement E; RadixTree mytree; auto zero = RCUPtr::make(0); auto one = RCUPtr::make(1); auto two = RCUPtr::make(2); auto three = RCUPtr::make(3); // Removing an element that isn't in the tree returns false. BOOST_CHECK(!mytree.remove(1)); // Insert an element into the tree and check you can remove it. BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); // Insert several elements and remove them. BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); BOOST_CHECK_EQUAL(mytree.get(0), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(2), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(3), NULLPTR(E)); // Once the elements are removed, removing them again returns false. BOOST_CHECK(!mytree.remove(0)); BOOST_CHECK(!mytree.remove(1)); BOOST_CHECK(!mytree.remove(2)); BOOST_CHECK(!mytree.remove(3)); // Check extreme values. typedef typename std::make_signed::type SK; K maxsk = std::numeric_limits::max(); K minsk = std::numeric_limits::min(); typedef typename std::make_unsigned::type UK; K maxuk = std::numeric_limits::max(); auto maxsigned = RCUPtr::make(maxsk); auto minsigned = RCUPtr::make(minsk); auto minusone = RCUPtr::make(maxuk); auto minustwo = RCUPtr::make(maxuk - 1); // Insert them all. BOOST_CHECK(mytree.insert(zero)); BOOST_CHECK(mytree.insert(one)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); BOOST_CHECK(mytree.insert(maxsigned)); BOOST_CHECK(mytree.insert(minsigned)); BOOST_CHECK(mytree.insert(minusone)); BOOST_CHECK(mytree.insert(minustwo)); // Delete them all BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); BOOST_CHECK(mytree.remove(maxsk)); BOOST_CHECK(mytree.remove(minsk)); BOOST_CHECK(mytree.remove(maxuk)); BOOST_CHECK(mytree.remove(maxuk - 1)); BOOST_CHECK_EQUAL(mytree.get(0), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(2), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(3), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(maxsk), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(minsk), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(maxuk), NULLPTR(E)); BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), NULLPTR(E)); } BOOST_AUTO_TEST_CASE(remove_test) { testRemove(); testRemove(); testRemove(); testRemove(); } BOOST_AUTO_TEST_CASE(const_element_test) { typedef const TestElement C; RadixTree mytree; BOOST_CHECK(mytree.insert(RCUPtr::make(0))); BOOST_CHECK(mytree.insert(RCUPtr::make(1))); BOOST_CHECK(mytree.insert(RCUPtr::make(2))); BOOST_CHECK(mytree.insert(RCUPtr::make(3))); BOOST_CHECK(!mytree.insert(RCUPtr::make(0))); BOOST_CHECK(!mytree.insert(RCUPtr::make(1))); BOOST_CHECK(!mytree.insert(RCUPtr::make(2))); BOOST_CHECK(!mytree.insert(RCUPtr::make(3))); BOOST_CHECK(mytree.get(0)); BOOST_CHECK(mytree.get(1)); BOOST_CHECK(mytree.get(2)); BOOST_CHECK(mytree.get(3)); BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); BOOST_CHECK(!mytree.get(0)); BOOST_CHECK(!mytree.get(1)); BOOST_CHECK(!mytree.get(2)); BOOST_CHECK(!mytree.get(3)); BOOST_CHECK(!mytree.remove(0)); BOOST_CHECK(!mytree.remove(1)); BOOST_CHECK(!mytree.remove(2)); BOOST_CHECK(!mytree.remove(3)); } void CheckConstTree(const RadixTree> &mytree, bool expected) { BOOST_CHECK_EQUAL(!!mytree.get(0), expected); BOOST_CHECK_EQUAL(!!mytree.get(1), expected); BOOST_CHECK_EQUAL(!!mytree.get(2), expected); BOOST_CHECK_EQUAL(!!mytree.get(3), expected); } BOOST_AUTO_TEST_CASE(const_tree_test) { typedef TestElement E; RadixTree mytree; CheckConstTree(mytree, false); BOOST_CHECK(mytree.insert(RCUPtr::make(0))); BOOST_CHECK(mytree.insert(RCUPtr::make(1))); BOOST_CHECK(mytree.insert(RCUPtr::make(2))); BOOST_CHECK(mytree.insert(RCUPtr::make(3))); CheckConstTree(mytree, true); BOOST_CHECK(mytree.remove(0)); BOOST_CHECK(mytree.remove(1)); BOOST_CHECK(mytree.remove(2)); BOOST_CHECK(mytree.remove(3)); CheckConstTree(mytree, false); } #define THREADS 128 #define ELEMENTS 65536 BOOST_AUTO_TEST_CASE(insert_stress_test) { typedef TestElement E; RadixTree mytree; std::atomic success{0}; std::vector threads; for (int i = 0; i < THREADS; i++) { threads.push_back(std::thread([&] { MMIXLinearCongruentialGenerator lcg; for (int j = 0; j < ELEMENTS; j++) { uint32_t v = lcg.next(); if (mytree.remove(v)) { success--; std::this_thread::yield(); } auto ptr = RCUPtr::make(v); if (mytree.insert(ptr)) { success++; std::this_thread::yield(); } if (mytree.remove(v)) { success--; std::this_thread::yield(); } if (mytree.insert(ptr)) { success++; std::this_thread::yield(); } } })); } // Wait for all the threads to finish. for (std::thread &t : threads) { t.join(); } BOOST_CHECK_EQUAL(success.load(), ELEMENTS); // All the elements have been inserted into the tree. MMIXLinearCongruentialGenerator lcg; for (int i = 0; i < ELEMENTS; i++) { uint32_t v = lcg.next(); BOOST_CHECK_EQUAL(mytree.get(v)->getId(), v); auto ptr = RCUPtr::make(v); BOOST_CHECK(!mytree.insert(ptr)); BOOST_CHECK(mytree.get(v) != ptr); } // Cleanup after ourselves. RCULock::synchronize(); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rcu_tests.cpp b/src/test/rcu_tests.cpp index cf4296e5f..eb763c5e3 100644 --- a/src/test/rcu_tests.cpp +++ b/src/test/rcu_tests.cpp @@ -1,400 +1,391 @@ // Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rcu.h" #include "test/test_bitcoin.h" #include #include struct RCUTest { static uint64_t getRevision() { return RCUInfos::revision.load(); } static uint64_t hasSyncedTo(uint64_t syncRev) { return RCUInfos::infos.hasSyncedTo(syncRev); } static std::map> &getCleanups() { return RCUInfos::infos.cleanups; } }; BOOST_FIXTURE_TEST_SUITE(rcu_tests, BasicTestingSetup) enum RCUTestStep { Init, Locked, LockAck, RCULocked, Synchronizing, Synchronized, }; #define WAIT_FOR_STEP(step) \ do { \ cond.notify_all(); \ } while (!cond.wait_for(lock, std::chrono::milliseconds(1), \ [&] { return otherstep == step; })) void synchronize(std::atomic &step, const std::atomic &otherstep, CWaitableCriticalSection &cs, std::condition_variable &cond, std::atomic &syncRev) { assert(step == RCUTestStep::Init); { WAIT_LOCK(cs, lock); step = RCUTestStep::Locked; // Wait for our lock to be acknowledged. WAIT_FOR_STEP(RCUTestStep::LockAck); RCULock rculock; // Update step. step = RCUTestStep::RCULocked; // Wait for master. WAIT_FOR_STEP(RCUTestStep::RCULocked); } // Update step. syncRev = RCUTest::getRevision() + 1; step = RCUTestStep::Synchronizing; assert(!RCUTest::hasSyncedTo(syncRev)); // We wait for readers. RCULock::synchronize(); // Update step. step = RCUTestStep::Synchronized; } void lockAndWaitForSynchronize(std::atomic &step, const std::atomic &otherstep, CWaitableCriticalSection &cs, std::condition_variable &cond, std::atomic &syncRev) { assert(step == RCUTestStep::Init); WAIT_LOCK(cs, lock); // Wait for th eother thread to be locked. WAIT_FOR_STEP(RCUTestStep::Locked); step = RCUTestStep::LockAck; // Wait for the synchronizing tread to take its RCU lock. WAIT_FOR_STEP(RCUTestStep::RCULocked); assert(!RCUTest::hasSyncedTo(syncRev)); { RCULock rculock; // Update master step. step = RCUTestStep::RCULocked; while (RCUTest::getRevision() < syncRev) { WAIT_FOR_STEP(RCUTestStep::Synchronizing); } assert(RCUTest::getRevision() >= syncRev); assert(otherstep.load() == RCUTestStep::Synchronizing); } assert(RCUTest::hasSyncedTo(syncRev) >= syncRev); WAIT_FOR_STEP(RCUTestStep::Synchronized); } static const int COUNT = 128; BOOST_AUTO_TEST_CASE(synchronize_test) { CWaitableCriticalSection cs; std::condition_variable cond; std::atomic parentstep; std::atomic childstep; std::atomic syncRev; for (int i = 0; i < COUNT; i++) { parentstep = RCUTestStep::Init; childstep = RCUTestStep::Init; syncRev = RCUTest::getRevision() + 1; std::thread tlock([&] { lockAndWaitForSynchronize(parentstep, childstep, cs, cond, syncRev); }); std::thread tsync( [&] { synchronize(childstep, parentstep, cs, cond, syncRev); }); tlock.join(); tsync.join(); } } -/** - * Version of Boost::test prior to 1.64 have issues when dealing with nullptr_t. - * In order to work around this, we ensure that the null pointers are typed in a - * way that Boost will like better. - * - * TODO: Use nullptr directly once the minimum version of boost is 1.64 or more. - */ -#define NULLPTR(T) static_cast(nullptr) - BOOST_AUTO_TEST_CASE(cleanup_test) { RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); bool isClean1 = false; RCULock::registerCleanup([&] { isClean1 = true; }); BOOST_CHECK(!isClean1); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 1); BOOST_CHECK_EQUAL(RCUTest::getRevision(), RCUTest::getCleanups().begin()->first); // Synchronize runs the cleanups. RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); // Check multiple callbacks. isClean1 = false; bool isClean2 = false; bool isClean3 = false; RCULock::registerCleanup([&] { isClean1 = true; }); RCULock::registerCleanup([&] { isClean2 = true; }); RCULock::registerCleanup([&] { isClean3 = true; }); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 3); RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); BOOST_CHECK(isClean2); BOOST_CHECK(isClean3); // Check callbacks adding each others. isClean1 = false; isClean2 = false; isClean3 = false; RCULock::registerCleanup([&] { isClean1 = true; RCULock::registerCleanup([&] { isClean2 = true; RCULock::registerCleanup([&] { isClean3 = true; }); }); }); BOOST_CHECK_EQUAL(RCUTest::getCleanups().size(), 1); RCULock::synchronize(); BOOST_CHECK(RCUTest::getCleanups().empty()); BOOST_CHECK(isClean1); BOOST_CHECK(isClean2); BOOST_CHECK(isClean3); } class RCURefTestItem { IMPLEMENT_RCU_REFCOUNT(uint32_t); const std::function cleanupfun; public: explicit RCURefTestItem(const std::function &fun) : cleanupfun(fun) {} ~RCURefTestItem() { cleanupfun(); } uint32_t getRefCount() const { return refcount.load(); } }; BOOST_AUTO_TEST_CASE(rcuptr_test) { // Make sure it works for null. { RCURefTestItem *ptr = nullptr; RCUPtr::copy(ptr); RCUPtr::acquire(ptr); } // Check the destruction mechanism. bool isDestroyed = false; { auto rcuptr = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 0); } // rcuptr waits for synchronization to destroy. BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); // Check that copy behaves properly. isDestroyed = false; RCUPtr gptr; { auto rcuptr = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 0); gptr = rcuptr; BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 1); BOOST_CHECK_EQUAL(gptr->getRefCount(), 1); auto rcuptrcopy = rcuptr; BOOST_CHECK_EQUAL(rcuptrcopy->getRefCount(), 2); BOOST_CHECK_EQUAL(rcuptr->getRefCount(), 2); BOOST_CHECK_EQUAL(gptr->getRefCount(), 2); } BOOST_CHECK_EQUAL(gptr->getRefCount(), 0); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); gptr = RCUPtr(); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } BOOST_AUTO_TEST_CASE(rcuptr_operator_test) { auto gptr = RCUPtr(); auto ptr = new RCURefTestItem([] {}); auto oldPtr = ptr; auto altptr = RCUPtr::make([] {}); // Check various operators. BOOST_CHECK_EQUAL(gptr.get(), NULLPTR(RCURefTestItem)); BOOST_CHECK_EQUAL(&*gptr, NULLPTR(RCURefTestItem)); BOOST_CHECK_EQUAL(gptr, NULLPTR(RCURefTestItem)); BOOST_CHECK(!gptr); auto copyptr = gptr; BOOST_CHECK(gptr == nullptr); BOOST_CHECK(gptr != oldPtr); BOOST_CHECK(gptr == copyptr); BOOST_CHECK(gptr != altptr); gptr = RCUPtr::acquire(ptr); BOOST_CHECK_EQUAL(ptr, NULLPTR(RCURefTestItem)); BOOST_CHECK_EQUAL(gptr.get(), oldPtr); BOOST_CHECK_EQUAL(&*gptr, oldPtr); BOOST_CHECK_EQUAL(gptr, oldPtr); BOOST_CHECK(gptr); copyptr = gptr; BOOST_CHECK(gptr != nullptr); BOOST_CHECK(gptr == oldPtr); BOOST_CHECK(gptr == copyptr); BOOST_CHECK(gptr != altptr); } BOOST_AUTO_TEST_CASE(const_rcuptr_test) { bool isDestroyed = false; auto ptr = RCUPtr::make([&] { isDestroyed = true; }); // Now let's destroy it. ptr = RCUPtr(); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } class RCURefMoveTestItem { const std::function cleanupfun; public: explicit RCURefMoveTestItem(const std::function &fun) : cleanupfun(fun) {} ~RCURefMoveTestItem() { cleanupfun(); } void acquire() { throw std::runtime_error("RCUPtr incremented the refcount"); } void release() { RCULock::registerCleanup([this] { delete this; }); } }; BOOST_AUTO_TEST_CASE(move_rcuptr_test) { bool isDestroyed = false; // Check tat copy is failing. auto rcuptr1 = RCUPtr::make([&] { isDestroyed = true; }); BOOST_CHECK_THROW(rcuptr1->acquire(), std::runtime_error); BOOST_CHECK_THROW(auto rcuptrcopy = rcuptr1;, std::runtime_error); // Try to move. auto rcuptr2 = std::move(rcuptr1); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Move to a local and check proper destruction. { auto rcuptr3 = std::move(rcuptr2); } BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); // Let's try to swap. isDestroyed = false; rcuptr1 = RCUPtr::make([&] { isDestroyed = true; }); std::swap(rcuptr1, rcuptr2); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Chain moves to make sure there are no double free. { auto rcuptr3 = std::move(rcuptr2); auto rcuptr4 = std::move(rcuptr3); std::swap(rcuptr1, rcuptr4); } RCULock::synchronize(); BOOST_CHECK(!isDestroyed); // Check we can return from a function. { auto r = ([&] { auto moved = std::move(rcuptr1); return moved; })(); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); } BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); // Acquire/release workflow. isDestroyed = false; auto ptr = new RCURefMoveTestItem([&] { isDestroyed = true; }); auto ptrCopy = ptr; BOOST_CHECK_THROW(RCUPtr::copy(ptr), std::runtime_error); rcuptr1 = RCUPtr::acquire(ptr); BOOST_CHECK_EQUAL(rcuptr1, ptrCopy); BOOST_CHECK_EQUAL(ptr, NULLPTR(RCURefMoveTestItem)); ptr = rcuptr1.release(); BOOST_CHECK_EQUAL(rcuptr1, NULLPTR(RCURefMoveTestItem)); BOOST_CHECK_EQUAL(ptr, ptrCopy); RCULock::synchronize(); BOOST_CHECK(!isDestroyed); RCUPtr::acquire(ptr); BOOST_CHECK_EQUAL(ptr, NULLPTR(RCURefMoveTestItem)); BOOST_CHECK(!isDestroyed); RCULock::synchronize(); BOOST_CHECK(isDestroyed); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 333a6ba72..ef6ead82d 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -1,156 +1,165 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_TEST_TEST_BITCOIN_H #define BITCOIN_TEST_TEST_BITCOIN_H #include "chainparamsbase.h" #include "fs.h" #include "key.h" #include "pubkey.h" #include "random.h" #include "scheduler.h" #include "txdb.h" #include "txmempool.h" #include +/** + * Version of Boost::test prior to 1.64 have issues when dealing with nullptr_t. + * In order to work around this, we ensure that the null pointers are typed in a + * way that Boost will like better. + * + * TODO: Use nullptr directly once the minimum version of boost is 1.64 or more. + */ +#define NULLPTR(T) static_cast(nullptr) + extern uint256 insecure_rand_seed; extern FastRandomContext insecure_rand_ctx; static inline void SeedInsecureRand(bool fDeterministic = false) { if (fDeterministic) { insecure_rand_seed = uint256(); } else { insecure_rand_seed = GetRandHash(); } insecure_rand_ctx = FastRandomContext(insecure_rand_seed); } static inline uint32_t insecure_rand() { return insecure_rand_ctx.rand32(); } static inline uint256 InsecureRand256() { return insecure_rand_ctx.rand256(); } static inline uint64_t InsecureRandBits(int bits) { return insecure_rand_ctx.randbits(bits); } static inline uint64_t InsecureRandRange(uint64_t range) { return insecure_rand_ctx.randrange(range); } static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); } static inline std::vector InsecureRandBytes(size_t len) { return insecure_rand_ctx.randbytes(len); } /** * Basic testing setup. * This just configures logging and chain parameters. */ struct BasicTestingSetup { ECCVerifyHandle globalVerifyHandle; explicit BasicTestingSetup( const std::string &chainName = CBaseChainParams::MAIN); ~BasicTestingSetup(); }; /** Testing setup that configures a complete environment. * Included are data directory, coins database, script check threads setup. */ class CConnman; class CNode; struct CConnmanTest { static void AddNode(CNode &node); static void ClearNodes(); }; class PeerLogicValidation; struct TestingSetup : public BasicTestingSetup { fs::path pathTemp; boost::thread_group threadGroup; CConnman *connman; CScheduler scheduler; std::unique_ptr peerLogic; explicit TestingSetup( const std::string &chainName = CBaseChainParams::MAIN); ~TestingSetup(); }; class CBlock; class CMutableTransaction; class CScript; // // Testing fixture that pre-creates a // 100-block REGTEST-mode block chain // struct TestChain100Setup : public TestingSetup { TestChain100Setup(); // Create a new block with just given transactions, coinbase paying to // scriptPubKey, and try to add it to the current chain. CBlock CreateAndProcessBlock(const std::vector &txns, const CScript &scriptPubKey); ~TestChain100Setup(); // For convenience, coinbase transactions. std::vector coinbaseTxns; // private/public key needed to spend coinbase transactions. CKey coinbaseKey; }; class CTxMemPoolEntry; class CTxMemPool; struct TestMemPoolEntryHelper { // Default values Amount nFee; int64_t nTime; double dPriority; unsigned int nHeight; bool spendsCoinbase; unsigned int sigOpCost; LockPoints lp; TestMemPoolEntryHelper() : nFee(), nTime(0), dPriority(0.0), nHeight(1), spendsCoinbase(false), sigOpCost(4) {} CTxMemPoolEntry FromTx(const CMutableTransaction &tx, CTxMemPool *pool = nullptr); CTxMemPoolEntry FromTx(const CTransaction &tx, CTxMemPool *pool = nullptr); // Change the default value TestMemPoolEntryHelper &Fee(Amount _fee) { nFee = _fee; return *this; } TestMemPoolEntryHelper &Time(int64_t _time) { nTime = _time; return *this; } TestMemPoolEntryHelper &Priority(double _priority) { dPriority = _priority; return *this; } TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; } }; #endif