diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -169,6 +169,7 @@ rpc/server.h \ rpc/tojson.h \ rpc/register.h \ + rwcollection.h \ scheduler.h \ script/scriptcache.h \ script/sigcache.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -79,6 +79,7 @@ test/random_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ + test/rwcollection_tests.cpp \ test/sanity_tests.cpp \ test/scheduler_tests.cpp \ test/script_commitment_tests.cpp \ diff --git a/src/rwcollection.h b/src/rwcollection.h new file mode 100644 --- /dev/null +++ b/src/rwcollection.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef BITCOIN_RWCOLLECTION_H +#define BITCOIN_RWCOLLECTION_H + +#include +#include +#include + +#include +#include +#include + +template class RWCollectionView : boost::noncopyable { +private: + L lock; + T *collection; + + template struct BracketType { + typedef decltype(std::declval()[std::declval()]) type; + }; + +public: + RWCollectionView(L l, T &c) : lock(std::move(l)), collection(&c) {} + RWCollectionView(RWCollectionView &&other) + : lock(std::move(other.lock)), collection(other.collection) {} + + T *operator->() { return collection; } + const T *operator->() const { return collection; } + + /** + * Iterator mechanics. + */ + typedef typename boost::range_iterator::type iterator; + iterator begin() { return std::begin(*collection); } + iterator end() { return std::end(*collection); } + + typedef typename boost::range_iterator::type const_iterator; + const_iterator begin() const { return std::begin(*collection); } + const_iterator end() const { return std::end(*collection); } + + /** + * Forward bracket operator. + */ + template typename BracketType::type operator[](I &&index) { + return (*collection)[std::forward(index)]; + } +}; + +template class RWCollection { +private: + mutable boost::shared_mutex rwlock; + T collection; + +public: + RWCollection() : collection() {} + + typedef RWCollectionView> + ReadView; + ReadView getReadView() const { + return ReadView(boost::shared_lock(rwlock), + collection); + } + + typedef RWCollectionView> + WriteView; + WriteView getWriteView() { + return WriteView(boost::unique_lock(rwlock), + collection); + } +}; + +#endif // BITCOIN_RWCOLLECTION_H diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -12,7 +12,7 @@ #include "uint256.h" #include "util.h" -#include +#include namespace { diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -96,6 +96,7 @@ random_tests.cpp reverselock_tests.cpp rpc_tests.cpp + rwcollection_tests.cpp sanity_tests.cpp scheduler_tests.cpp script_commitment_tests.cpp diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -7,7 +7,7 @@ #include "script/sigcache.h" #include "test/test_bitcoin.h" -#include +#include #include diff --git a/src/test/rwcollection_tests.cpp b/src/test/rwcollection_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/rwcollection_tests.cpp @@ -0,0 +1,135 @@ +// 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 "rwcollection.h" + +#include "test/test_bitcoin.h" + +#include +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(rwcollection_tests); + +BOOST_AUTO_TEST_CASE(vector) { + RWCollection> rwvector; + + { + auto w = rwvector.getWriteView(); + for (int i = 0; i < 100; i++) { + w->push_back(i + 10); + BOOST_CHECK_EQUAL(w[i], i + 10); + BOOST_CHECK_EQUAL(w->back(), i + 10); + BOOST_CHECK_EQUAL(w->size(), i + 1); + + w[i] = i; + BOOST_CHECK_EQUAL(w[i], i); + } + + int e = 0; + for (int &i : w) { + BOOST_CHECK_EQUAL(i, e++); + i *= 2; + } + + e = 0; + for (int &i : boost::adaptors::reverse(w)) { + BOOST_CHECK_EQUAL(i, 198 - 2 * e); + i = e++; + } + } + + { + auto r = rwvector.getReadView(); + for (int i = 0; i < 100; i++) { + BOOST_CHECK_EQUAL(r[i], 99 - i); + } + + int e = 0; + for (const int &i : r) { + BOOST_CHECK_EQUAL(i, 99 - (e++)); + } + + e = 0; + for (const int &i : boost::adaptors::reverse(r)) { + BOOST_CHECK_EQUAL(i, e++); + } + } +} + +BOOST_AUTO_TEST_CASE(set) { + RWCollection> rwset; + + { + auto w = rwset.getWriteView(); + for (int i = 0; i < 100; i++) { + w->insert(i); + BOOST_CHECK_EQUAL(w->count(i), 1); + BOOST_CHECK_EQUAL(*(w->find(i)), i); + BOOST_CHECK_EQUAL(w->size(), i + 1); + } + + for (int i = 0; i < 100; i += 2) { + BOOST_CHECK_EQUAL(w->erase(i), 1); + BOOST_CHECK_EQUAL(w->count(i), 0); + BOOST_CHECK(w->find(i) == std::end(w)); + } + + int e = 0; + for (const int &i : w) { + BOOST_CHECK_EQUAL(i / 2, e++); + } + } + + { + auto r = rwset.getReadView(); + for (int i = 0; i < 100; i += 2) { + BOOST_CHECK_EQUAL(r->count(i), 0); + BOOST_CHECK(r->find(i) == std::end(r)); + } + + for (int i = 1; i < 100; i += 2) { + BOOST_CHECK_EQUAL(r->count(i), 1); + BOOST_CHECK_EQUAL(*(r->find(i)), i); + } + + int e = 0; + for (const int &i : r) { + BOOST_CHECK_EQUAL(i / 2, e++); + } + } +} + +BOOST_AUTO_TEST_CASE(map) { + RWCollection> rwmap; + + { + auto w = rwmap.getWriteView(); + w["1"] = "one"; + w["2"] = "two"; + w["3"] = "three"; + BOOST_CHECK_EQUAL(w["1"], "one"); + BOOST_CHECK_EQUAL(w["2"], "two"); + BOOST_CHECK_EQUAL(w["3"], "three"); + + for (const std::pair &p : w) { + BOOST_CHECK_EQUAL(w[p.first], p.second); + } + } + + { + auto r = rwmap.getReadView(); + BOOST_CHECK_EQUAL(r->count("1"), 1); + BOOST_CHECK_EQUAL(r->find("1")->first, "1"); + BOOST_CHECK_EQUAL(r->find("1")->second, "one"); + + for (const std::pair &p : r) { + BOOST_CHECK_EQUAL(r->at(p.first), p.second); + } + } +} + +BOOST_AUTO_TEST_SUITE_END();