diff --git a/builds/cmake/install-cmake.sh b/builds/cmake/install-cmake.sh index 31eb60046..54f9b3769 100755 --- a/builds/cmake/install-cmake.sh +++ b/builds/cmake/install-cmake.sh @@ -446,21 +446,23 @@ main() UltrafastSecp256k1_FLAGS=() UltrafastSecp256k1_OPTIONS=( - "-DSECP256K1_BUILD_TESTS=OFF" - "-DSECP256K1_BUILD_BENCH=OFF" - "-DSECP256K1_BUILD_EXAMPLES=OFF" - "-DSECP256K1_BUILD_JAVA=OFF" - "-DUFSECP_BUILD_SHARED=OFF" "-DSECP256K1_BUILD_CABI=ON" "-DSECP256K1_BUILD_CPU=ON" "-DSECP256K1_BUILD_SHIM=ON" + "-DSECP256K1_USE_ULTRAFAST=ON" "-DSECP256K1_BUILD_CUDA=ON" "-DSECP256K1_BUILD_ROCM=ON" "-DSECP256K1_BUILD_OPENCL=ON" "-DSECP256K1_BUILD_METAL=ON" + "-DSECP256K1_BUILD_BENCH=OFF" + "-DSECP256K1_BUILD_EXAMPLES=OFF" + "-DSECP256K1_BUILD_JAVA=OFF" + "-DSECP256K1_BUILD_TESTS=OFF" + "-DSECP256K1_SHIM_BUILD_SHARED=OFF" + "-DUFSECP_BUILD_SHARED=OFF" "-DSECP256K1_INSTALL=ON" - "-DSECP256K1_INSTALL_PKGCONFIG=ON" - "-DSECP256K1_USE_ULTRAFAST=ON") + "-DSECP256K1_SHIM_INSTALL=ON" + "-DSECP256K1_INSTALL_PKGCONFIG=ON") libbitcoin_system_FLAGS=() diff --git a/builds/cmake/install-presets.sh b/builds/cmake/install-presets.sh index a16a2dbc5..700ec5c99 100755 --- a/builds/cmake/install-presets.sh +++ b/builds/cmake/install-presets.sh @@ -462,21 +462,23 @@ main() UltrafastSecp256k1_FLAGS=() UltrafastSecp256k1_OPTIONS=( - "-DSECP256K1_BUILD_TESTS=OFF" - "-DSECP256K1_BUILD_BENCH=OFF" - "-DSECP256K1_BUILD_EXAMPLES=OFF" - "-DSECP256K1_BUILD_JAVA=OFF" - "-DUFSECP_BUILD_SHARED=OFF" "-DSECP256K1_BUILD_CABI=ON" "-DSECP256K1_BUILD_CPU=ON" "-DSECP256K1_BUILD_SHIM=ON" + "-DSECP256K1_USE_ULTRAFAST=ON" "-DSECP256K1_BUILD_CUDA=ON" "-DSECP256K1_BUILD_ROCM=ON" "-DSECP256K1_BUILD_OPENCL=ON" "-DSECP256K1_BUILD_METAL=ON" + "-DSECP256K1_BUILD_BENCH=OFF" + "-DSECP256K1_BUILD_EXAMPLES=OFF" + "-DSECP256K1_BUILD_JAVA=OFF" + "-DSECP256K1_BUILD_TESTS=OFF" + "-DSECP256K1_SHIM_BUILD_SHARED=OFF" + "-DUFSECP_BUILD_SHARED=OFF" "-DSECP256K1_INSTALL=ON" - "-DSECP256K1_INSTALL_PKGCONFIG=ON" - "-DSECP256K1_USE_ULTRAFAST=ON") + "-DSECP256K1_SHIM_INSTALL=ON" + "-DSECP256K1_INSTALL_PKGCONFIG=ON") libbitcoin_system_FLAGS=() diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am index 066f222b7..779dd0c99 100644 --- a/builds/gnu/Makefile.am +++ b/builds/gnu/Makefile.am @@ -91,7 +91,6 @@ include_bitcoin_database_impl_memorydir = \ ${includedir}/bitcoin/database/impl/memory include_bitcoin_database_impl_memory_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/impl/memory/accessor.ipp \ ${srcdir}/../../include/bitcoin/database/impl/memory/mmap.ipp \ ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_dispatch.ipp \ ${srcdir}/../../include/bitcoin/database/impl/memory/mmap_private.ipp \ @@ -231,7 +230,6 @@ include_bitcoin_database_memory_interfacesdir = \ ${includedir}/bitcoin/database/memory/interfaces include_bitcoin_database_memory_interfaces_HEADERS = \ - ${srcdir}/../../include/bitcoin/database/memory/interfaces/memory.hpp \ ${srcdir}/../../include/bitcoin/database/memory/interfaces/storage.hpp include_bitcoin_database_primitivesdir = \ diff --git a/builds/gnu/install-gnu.sh b/builds/gnu/install-gnu.sh index 1a67860ee..149ed7be1 100755 --- a/builds/gnu/install-gnu.sh +++ b/builds/gnu/install-gnu.sh @@ -474,21 +474,23 @@ main() UltrafastSecp256k1_FLAGS=() UltrafastSecp256k1_OPTIONS=( - "-DSECP256K1_BUILD_TESTS=OFF" - "-DSECP256K1_BUILD_BENCH=OFF" - "-DSECP256K1_BUILD_EXAMPLES=OFF" - "-DSECP256K1_BUILD_JAVA=OFF" - "-DUFSECP_BUILD_SHARED=OFF" "-DSECP256K1_BUILD_CABI=ON" "-DSECP256K1_BUILD_CPU=ON" "-DSECP256K1_BUILD_SHIM=ON" + "-DSECP256K1_USE_ULTRAFAST=ON" "-DSECP256K1_BUILD_CUDA=ON" "-DSECP256K1_BUILD_ROCM=ON" "-DSECP256K1_BUILD_OPENCL=ON" "-DSECP256K1_BUILD_METAL=ON" + "-DSECP256K1_BUILD_BENCH=OFF" + "-DSECP256K1_BUILD_EXAMPLES=OFF" + "-DSECP256K1_BUILD_JAVA=OFF" + "-DSECP256K1_BUILD_TESTS=OFF" + "-DSECP256K1_SHIM_BUILD_SHARED=OFF" + "-DUFSECP_BUILD_SHARED=OFF" "-DSECP256K1_INSTALL=ON" - "-DSECP256K1_INSTALL_PKGCONFIG=ON" - "-DSECP256K1_USE_ULTRAFAST=ON") + "-DSECP256K1_SHIM_INSTALL=ON" + "-DSECP256K1_INSTALL_PKGCONFIG=ON") libbitcoin_system_FLAGS=() diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj index 975320042..efedb59c9 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj @@ -153,7 +153,6 @@ - @@ -222,7 +221,6 @@ - diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters index e6d4b5e27..0d8fcc5f1 100644 --- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -176,9 +176,6 @@ include\bitcoin\database\memory - - include\bitcoin\database\memory\interfaces - include\bitcoin\database\memory\interfaces @@ -379,9 +376,6 @@ - - include\bitcoin\database\impl\memory - include\bitcoin\database\impl\memory diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj index 15d03af47..3cba41c76 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj @@ -153,7 +153,6 @@ - @@ -222,7 +221,6 @@ - diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters index e6d4b5e27..0d8fcc5f1 100644 --- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters +++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters @@ -176,9 +176,6 @@ include\bitcoin\database\memory - - include\bitcoin\database\memory\interfaces - include\bitcoin\database\memory\interfaces @@ -379,9 +376,6 @@ - - include\bitcoin\database\impl\memory - include\bitcoin\database\impl\memory diff --git a/include/bitcoin/database.hpp b/include/bitcoin/database.hpp index 8aa84be17..2989b4496 100644 --- a/include/bitcoin/database.hpp +++ b/include/bitcoin/database.hpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/include/bitcoin/database/impl/memory/accessor.ipp b/include/bitcoin/database/impl/memory/accessor.ipp deleted file mode 100644 index ddf104746..000000000 --- a/include/bitcoin/database/impl/memory/accessor.ipp +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_MEMORY_ACCESSOR_IPP -#define LIBBITCOIN_DATABASE_MEMORY_ACCESSOR_IPP - -////#include -#include - -// Zero/negative size is allowed (automatically handled by bc streams). - -namespace libbitcoin { -namespace database { - -BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - -TEMPLATE -inline CLASS::accessor(Mutex& mutex) NOEXCEPT - : shared_lock_(mutex) -{ -} - -TEMPLATE -inline void CLASS::assign(uint8_t* begin, uint8_t* end) NOEXCEPT -{ - begin_ = begin; - end_ = end; - ////BC_ASSERT(!system::is_negative(size())); -} - -TEMPLATE -inline uint8_t* CLASS::offset(size_t bytes) const NOEXCEPT -{ - if (system::is_greater(bytes, size())) - return nullptr; - - BC_PUSH_WARNING(NO_POINTER_ARITHMETIC) - return begin_ + bytes; - BC_POP_WARNING() - ////return std::next(begin_, bytes); -} - -TEMPLATE -inline ptrdiff_t CLASS::size() const NOEXCEPT -{ - BC_PUSH_WARNING(NO_POINTER_ARITHMETIC) - return system::possible_narrow_and_sign_cast(end_ - begin_); - BC_POP_WARNING() - ////return std::distance(begin_, end_); -} - -TEMPLATE -inline uint8_t* CLASS::data() const NOEXCEPT -{ - return begin(); -} - -TEMPLATE -inline CLASS::operator system::data_slab() const NOEXCEPT -{ - return { begin(), end() }; -}; - -TEMPLATE -inline uint8_t* CLASS::begin() const NOEXCEPT -{ - return begin_; -} - -TEMPLATE -inline uint8_t* CLASS::end() const NOEXCEPT -{ - return end_; -} - -BC_POP_WARNING() - -} // namespace database -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/database/impl/memory/mmap_dispatch.ipp b/include/bitcoin/database/impl/memory/mmap_dispatch.ipp index efbbdeec9..f2ca4b458 100644 --- a/include/bitcoin/database/impl/memory/mmap_dispatch.ipp +++ b/include/bitcoin/database/impl/memory/mmap_dispatch.ipp @@ -33,30 +33,6 @@ namespace database { // Dispatch. // ---------------------------------------------------------------------------- -TEMPLATE -memory_ptr CLASS::get_capacity(size_t offset) const NOEXCEPT -{ - const auto allocated = to_width(capacity()); - - const auto ptr = std::make_shared(remap_mutex_); - if (!loaded_ || is_null(ptr)) - return {}; - - auto data = memory_map_.front(); - ptr->assign(std::next(data, offset), std::next(data, allocated)); - return ptr; -} - -TEMPLATE -memory::iterator CLASS::get_raw(size_t offset) const NOEXCEPT -{ - // Pointer otherwise unguarded, not remap safe (use for fixed table heads). - if (offset > to_width(size())) - return {}; - - return std::next(memory_map_.front(), offset); -} - TEMPLATE memory_ptr CLASS::set(size_t offset, size_t size, uint8_t backfill) NOEXCEPT { @@ -93,6 +69,37 @@ memory_ptr CLASS::set(size_t offset, size_t size, uint8_t backfill) NOEXCEPT return get(offset); } +TEMPLATE +memory_ptr CLASS::get_capacity(size_t offset) const NOEXCEPT +{ + const auto allocated = to_width(capacity()); + + const auto ptr = std::make_shared(remap_mutex_); + if (!loaded_ || is_null(ptr)) + return {}; + + auto data = memory_map_.front(); + ptr->assign(std::next(data, offset), std::next(data, allocated)); + return ptr; +} + +TEMPLATE +memory::iterator CLASS::get_raw(size_t offset) const NOEXCEPT +{ + // Pointer otherwise unguarded, not remap safe (use for fixed table heads). + return get_at_raw(zero, offset); +} + +TEMPLATE +memory::iterator CLASS::get_at_raw(size_t column, size_t offset) const NOEXCEPT +{ + // get_raw not used for variably-sized heads, so should always be bounded. + BC_ASSERT(offset < (size() * widths.at(column))); + + // Pointer otherwise unguarded, not remap safe (use for nopmaps columns). + return std::next(memory_map_.at(column), offset); +} + TEMPLATE memory_ptr CLASS::get(size_t offset) const NOEXCEPT { @@ -109,7 +116,7 @@ memory_ptr CLASS::get_at(size_t column, size_t offset) const NOEXCEPT const auto allocated = size() * widths.at(column); // Takes a shared lock on remap_mutex_ until destruct, blocking remap. - const auto ptr = std::make_shared(remap_mutex_); + const auto ptr = std::make_shared(remap_mutex_); // loaded_ update is precluded by above lock, making this read atomic. if (!loaded_ || is_null(ptr)) @@ -121,6 +128,34 @@ memory_ptr CLASS::get_at(size_t column, size_t offset) const NOEXCEPT return ptr; } +TEMPLATE +memory CLASS::get1(size_t offset) const NOEXCEPT +{ + return get_at1(zero, offset); +} + +TEMPLATE +memory CLASS::get_at1(size_t column, size_t offset) const NOEXCEPT +{ + if (column >= columns) + return {}; + + // Obtaining size before access prevents mutual mutex wait (deadlock). + const auto allocated = size() * widths.at(column); + + // Takes a shared lock on remap_mutex_ until destruct, blocking remap. + memory out{ remap_mutex_ }; + + // loaded_ update is precluded by above lock, making this read atomic. + if (!loaded_) + return {}; + + // With offset > size the assignment is negative (stream is exhausted). + auto data = memory_map_.at(column); + out.assign(std::next(data, offset), std::next(data, allocated)); + return out; +} + } // namespace database } // namespace libbitcoin diff --git a/include/bitcoin/database/impl/primitives/manager.ipp b/include/bitcoin/database/impl/primitives/manager.ipp index 834b6a4e1..a1a0b0cba 100644 --- a/include/bitcoin/database/impl/primitives/manager.ipp +++ b/include/bitcoin/database/impl/primitives/manager.ipp @@ -34,12 +34,22 @@ inline memory_ptr CLASS::get() const NOEXCEPT return files_.get_at(Column); } +TEMPLATE +template +inline memory CLASS::get1() const NOEXCEPT +{ + if constexpr (is_one(columns)) + return files_.get1(); + else + return files_.get_at1(Column); +} + TEMPLATE template inline memory_ptr CLASS::get(const Link& link) const NOEXCEPT { if (link.is_terminal()) - return nullptr; + return {}; const auto position = link_to_position(link); @@ -50,12 +60,44 @@ inline memory_ptr CLASS::get(const Link& link) const NOEXCEPT return files_.get_at(Column, position); } +TEMPLATE +template +inline memory CLASS::get1(const Link& link) const NOEXCEPT +{ + if (link.is_terminal()) + return {}; + + const auto position = link_to_position(link); + + // memory.size() may be negative (stream treats as exhausted). + if constexpr (is_one(columns)) + return files_.get1(position); + else + return files_.get_at1(Column, position); +} + +TEMPLATE +template +inline memory::iterator CLASS::get_raw1(const Link& link) const NOEXCEPT +{ + if (link.is_terminal()) + return {}; + + const auto position = link_to_position(link); + + // memory.size() may be negative (stream treats as exhausted). + if constexpr (is_one(columns)) + return files_.get_raw(position); + else + return files_.get_at_raw(Column, position); +} + TEMPLATE template > inline memory_ptr CLASS::get_capacity(const Link& link) const NOEXCEPT { if (link.is_terminal()) - return nullptr; + return {}; return files_.get_capacity(link_to_position(link)); } diff --git a/include/bitcoin/database/impl/primitives/nomaps.ipp b/include/bitcoin/database/impl/primitives/nomaps.ipp index a649e461a..39f59c2c9 100644 --- a/include/bitcoin/database/impl/primitives/nomaps.ipp +++ b/include/bitcoin/database/impl/primitives/nomaps.ipp @@ -128,34 +128,39 @@ code CLASS::reload() NOEXCEPT // query interface // ---------------------------------------------------------------------------- +TEMPLATE +memory CLASS::guard() const NOEXCEPT +{ + return get_memory(); +} + TEMPLATE template -memory_ptr CLASS::get_memory() const NOEXCEPT +memory CLASS::get_memory() const NOEXCEPT { - return manager_.template get(); + return manager_.template get1(); } // static TEMPLATE template -bool CLASS::get(const memory_ptr& ptr, const Link& link, - Element& element) NOEXCEPT +bool CLASS::get(const memory& ptr, const Link& link, Element& element) NOEXCEPT { - static_assert(Element::size == width, "element size != width"); - using namespace system; + static_assert(Element::size == width); if (!ptr || link.is_terminal()) return false; + using namespace system; const auto start = body::template link_to_position(link); if (is_limited(start)) return false; - const auto size = ptr->size(); + const auto size = ptr.size(); const auto position = possible_narrow_and_sign_cast(start); if (position >= size) return false; - const auto offset = ptr->offset(start); + const auto offset = ptr.offset(start); if (is_null(offset)) return false; @@ -166,6 +171,7 @@ bool CLASS::get(const memory_ptr& ptr, const Link& link, return element.from_data(source); } +// TODO: gets are not optimized for shared remap guard. TEMPLATE template bool CLASS::get(const Link& link, Element& element) const NOEXCEPT @@ -173,25 +179,26 @@ bool CLASS::get(const Link& link, Element& element) const NOEXCEPT return get(get_memory(), link, element); } +// TODO: puts are optimized for shared remap guard (required). TEMPLATE template bool CLASS::put(const Link& link, const Element& element) NOEXCEPT { - using namespace system; - const auto ptr = manager_.template get(link); + const auto ptr = manager_.template get_raw1(link); return put(ptr, element); } +// protected (unguarded memory access) TEMPLATE template -bool CLASS::put(const memory_ptr& ptr, const Element& element) NOEXCEPT +bool CLASS::put(memory::iterator it, const Element& element) NOEXCEPT { - static_assert(Element::size == width, "element size != width"); - using namespace system; - if (!ptr) + static_assert(Element::size == width); + if (is_null(it)) return false; - iostream stream{ *ptr }; + using namespace system; + iostream stream{ it, system::maximum }; flipper sink{ stream }; BC_DEBUG_ONLY(sink.set_limit(width * element.count());) diff --git a/include/bitcoin/database/impl/query/batch/ecdsa.ipp b/include/bitcoin/database/impl/query/batch/ecdsa.ipp index d4d0f3087..be2f522fb 100644 --- a/include/bitcoin/database/impl/query/batch/ecdsa.ipp +++ b/include/bitcoin/database/impl/query/batch/ecdsa.ipp @@ -41,10 +41,10 @@ bool CLASS::verify_ecdsa_signatures(const stopper& cancel, using signature_t = const table::ecdsa_signature::span; using namespace system; - const auto correlate = pointer_cast(correlate_ptr->data()); - const auto digest = pointer_cast(digest_ptr->data()); - const auto compressed = pointer_cast(compressed_ptr->data()); - const auto signature = pointer_cast(signature_ptr->data()); + const auto correlate = pointer_cast(correlate_ptr.data()); + const auto digest = pointer_cast(digest_ptr.data()); + const auto compressed = pointer_cast(compressed_ptr.data()); + const auto signature = pointer_cast(signature_ptr.data()); // Shortest column. const auto count = store_.ecdsa.count(); @@ -91,11 +91,13 @@ bool CLASS::set_signature(const hash_digest& digest, const auto row = possible_narrow_cast(one); // Allocate 1 row across all columns. - // TODO: this could provide a single remap lock for all puts below. const auto fk = store_.ecdsa.allocate(row); if (fk.is_terminal()) return false; + // Guard against remap (required for nomaps::put(fk)). + const auto guard = store_.ecdsa.guard(); + // Write one value to each column in corresponding positions. return store_.ecdsa.correlate.put(fk, correlate_t{ {}, link, id }) && @@ -116,23 +118,25 @@ bool CLASS::set_signatures(const hash_digest& digest, using compressed_t = table::ecdsa_compressed::put_refs; using signature_t = table::ecdsa_signature::put_refs; + using namespace system; const auto csigs = sigs.size(); const auto ckeys = keys.size(); - const auto count = system::chain::multisig::rows(csigs, ckeys); + const auto count = chain::multisig::rows(csigs, ckeys); // Caller must guard reads, this is writing into hot storage. // ======================================================================== const auto scope = get_transactor(); - using namespace system; const auto rows = possible_narrow_cast(count); // Allocate rows across all columns. - // TODO: this could provide a single remap lock for all puts below. const auto fk = store_.ecdsa.allocate(rows); if (fk.is_terminal()) return false; + // Guard against remap (required for nomaps::put(fk)). + const auto guard = store_.ecdsa.guard(); + // Write values to each column in corresponding positions. return store_.ecdsa.correlate.put(fk, correlate_t{ {}, count, link, ckeys, csigs, id }) && diff --git a/include/bitcoin/database/impl/query/batch/schnorr.ipp b/include/bitcoin/database/impl/query/batch/schnorr.ipp index 6916cf47c..d8189585a 100644 --- a/include/bitcoin/database/impl/query/batch/schnorr.ipp +++ b/include/bitcoin/database/impl/query/batch/schnorr.ipp @@ -40,10 +40,10 @@ bool CLASS::verify_schnorr_signatures(const stopper& cancel, using signature_t = const table::schnorr_signature::span; using namespace system; - const auto correlate = pointer_cast(correlate_ptr->data()); - const auto digest = pointer_cast(digest_ptr->data()); - const auto xonly = pointer_cast(xonly_ptr->data()); - const auto signature = pointer_cast(signature_ptr->data()); + const auto correlate = pointer_cast(correlate_ptr.data()); + const auto digest = pointer_cast(digest_ptr.data()); + const auto xonly = pointer_cast(xonly_ptr.data()); + const auto signature = pointer_cast(signature_ptr.data()); // Shortest column. const auto count = store_.schnorr.count(); @@ -89,11 +89,13 @@ bool CLASS::set_signature(const hash_digest& digest, const ec_xonly& point, const auto row = possible_narrow_cast(one); // Allocate 1 row across all columns. - // TODO: this could provide a single remap lock for all puts below. const auto fk = store_.schnorr.allocate(row); if (fk.is_terminal()) return false; + // Guard against remap (required for nomaps::put(fk)). + const auto guard = store_.schnorr.guard(); + // Write one value to each column in corresponding positions. return store_.schnorr.correlate.put(fk, correlate_t{ {}, link }) && @@ -131,6 +133,9 @@ bool CLASS::set_signature(const schnorr_link& schnorr_fk, // ======================================================================== const auto scope = get_transactor(); + // Guard against remap (required for nomaps::put(fk)). + const auto guard = store_.schnorr.guard(); + return store_.schnorr.correlate.put(schnorr_fk, correlate_t{ {}, link }) && store_.schnorr.digest.put(schnorr_fk, digest_t{ {}, digest }) && diff --git a/include/bitcoin/database/impl/query/batch/silent.ipp b/include/bitcoin/database/impl/query/batch/silent.ipp index 3813db19e..36bab992c 100644 --- a/include/bitcoin/database/impl/query/batch/silent.ipp +++ b/include/bitcoin/database/impl/query/batch/silent.ipp @@ -40,9 +40,9 @@ bool CLASS::scan_silent(const stopper& cancel, const ec_secret& scan_key, using compressed_t = const table::silent_compressed::span; using namespace system; - const auto correlate = pointer_cast(correlate_ptr->data()); - const auto prefix = pointer_cast(prefix_ptr->data()); - const auto compressed = pointer_cast(compressed_ptr->data()); + const auto correlate = pointer_cast(correlate_ptr.data()); + const auto prefix = pointer_cast(prefix_ptr.data()); + const auto compressed = pointer_cast(compressed_ptr.data()); // Shortest column. const auto count = store_.silent.count(); @@ -117,7 +117,10 @@ bool CLASS::set_silent(const tx_link& link, using prefix_t = table::silent_prefix::put_ref; using compressed_t = table::silent_compressed::put_ref; - // TODO: caller must guard reads, this is writing into hot storage. + // TODO: Caller must guard reads, this is writing into hot storage. This + // TODO: requires caller to chase writers and account for the last contig- + // TODO: uously populated row (for searching) and to update subscriptions + // TODO: with additional scans as this position increases. // ======================================================================== const auto scope = get_transactor(); @@ -125,11 +128,13 @@ bool CLASS::set_silent(const tx_link& link, auto rows = possible_narrow_cast(prefixes.size()); // Allocate rows across all columns. - // TODO: this could provide a single remap lock for all puts below. const auto fk = store_.silent.allocate(rows); if (fk.is_terminal()) return false; + // Guard against remap (required for nomaps::put(fk)). + const auto guard = store_.silent.guard(); + // Write values to each column in corresponding positions. return store_.silent.correlate.put(fk, correlate_t{ {}, rows, link }) && diff --git a/include/bitcoin/database/memory/accessor.hpp b/include/bitcoin/database/memory/accessor.hpp index 4fe08225a..c4a1cb541 100644 --- a/include/bitcoin/database/memory/accessor.hpp +++ b/include/bitcoin/database/memory/accessor.hpp @@ -21,57 +21,109 @@ #include #include -#include namespace libbitcoin { namespace database { /// Shared r/w access to a memory buffer, mutex blocks memory remap. -template -class accessor - : public memory +/// Zero/negative size is allowed (automatically handled by bc streams). +class accessor final { public: - DELETE_COPY_MOVE_DESTRUCT(accessor); + typedef uint8_t value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + typedef std::shared_ptr ptr; + + DELETE_COPY(accessor); + DEFAULT_MOVE(accessor); + + /// Fault construction, invalid object, no lock taken. + inline accessor() NOEXCEPT + : shared_lock_{} + { + } /// Mutex guards against remap while object in scope. - inline accessor(Mutex& mutex) NOEXCEPT; + inline accessor(std::shared_mutex& mutex) NOEXCEPT + : shared_lock_{ mutex } + { + } /// Set the buffer, where end is within allocated space. /// End should be initialized to logical space though that may contract or /// expand during accessor lifetime. The only guarantee offered by end is /// that it remains within allocated space and is initially logical space. - inline void assign(uint8_t* begin, uint8_t* end) NOEXCEPT; + inline void assign(uint8_t* begin, uint8_t* end) NOEXCEPT + { + begin_ = begin; + end_ = end; + ////BC_ASSERT(!system::is_negative(size())); + } + + /// memory interface + /// ----------------------------------------------------------------------- + + inline operator bool() const NOEXCEPT + { + return !is_null(begin_); + } /// Return an offset from begin, nullptr if end or past end. - inline uint8_t* offset(size_t bytes) const NOEXCEPT override; + inline uint8_t* offset(size_t bytes) const NOEXCEPT + { + if (system::is_greater(bytes, size())) + return nullptr; + + BC_PUSH_WARNING(NO_POINTER_ARITHMETIC) + return begin_ + bytes; + BC_POP_WARNING() + ////return std::next(begin_, bytes); + } /// The logical buffer size (from begin to end). - inline ptrdiff_t size() const NOEXCEPT override; + inline ptrdiff_t size() const NOEXCEPT + { + BC_PUSH_WARNING(NO_POINTER_ARITHMETIC) + return system::possible_narrow_and_sign_cast(end_ - begin_); + BC_POP_WARNING() + ////return std::distance(begin_, end_); + } /// Alias for begin. - inline uint8_t* data() const NOEXCEPT override; + inline uint8_t* data() const NOEXCEPT + { + return begin(); + } /// Get logical buffer (guarded against remap only). - inline operator system::data_slab() const NOEXCEPT override; - inline uint8_t* begin() const NOEXCEPT override; - inline uint8_t* end() const NOEXCEPT override; + inline operator system::data_slab() const NOEXCEPT + { + return { begin(), end() }; + }; + + /// Buffer start. + inline uint8_t* begin() const NOEXCEPT + { + return begin_; + } + + /// Buffer end. + inline uint8_t* end() const NOEXCEPT + { + return end_; + } private: uint8_t* begin_{}; uint8_t* end_{}; - std::shared_lock shared_lock_; + std::shared_lock shared_lock_; }; +using memory = accessor; +using memory_ptr = accessor::ptr; + } // namespace database } // namespace libbitcoin -#define TEMPLATE template -#define CLASS accessor - -#include - -#undef CLASS -#undef TEMPLATE - #endif diff --git a/include/bitcoin/database/memory/interfaces/memory.hpp b/include/bitcoin/database/memory/interfaces/memory.hpp deleted file mode 100644 index d033c5c91..000000000 --- a/include/bitcoin/database/memory/interfaces/memory.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2011-2026 libbitcoin developers (see AUTHORS) - * - * This file is part of libbitcoin. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -#ifndef LIBBITCOIN_DATABASE_MEMORY_INTERFACES_MEMORY_HPP -#define LIBBITCOIN_DATABASE_MEMORY_INTERFACES_MEMORY_HPP - -#include -#include - -namespace libbitcoin { -namespace database { - -/// Protected memory access interface. -class memory -{ -public: - typedef uint8_t value_type; - typedef value_type* iterator; - typedef const value_type* const_iterator; - typedef std::shared_ptr ptr; - - /// Return an offset from begin, nullptr if end or past end. - virtual uint8_t* offset(size_t value) const NOEXCEPT = 0; - - /// The logical buffer size (from begin to end). - virtual ptrdiff_t size() const NOEXCEPT = 0; - - /// Alias begin. - virtual uint8_t* data() const NOEXCEPT = 0; - - /// Get logical buffer. - virtual operator system::data_slab() const NOEXCEPT = 0; - virtual uint8_t* begin() const NOEXCEPT = 0; - virtual uint8_t* end() const NOEXCEPT = 0; -}; - -typedef memory::ptr memory_ptr; - -} // namespace database -} // namespace libbitcoin - -#endif diff --git a/include/bitcoin/database/memory/interfaces/storage.hpp b/include/bitcoin/database/memory/interfaces/storage.hpp index e190c1ebb..7c7343dad 100644 --- a/include/bitcoin/database/memory/interfaces/storage.hpp +++ b/include/bitcoin/database/memory/interfaces/storage.hpp @@ -21,7 +21,7 @@ #include #include -#include +#include namespace libbitcoin { namespace database { @@ -88,6 +88,10 @@ class storage /// Increase logical by specified rows/bytes, return row of first (or eof). virtual size_t allocate(size_t count) NOEXCEPT = 0; + /// Get remap-protected r/w access to offset (or null) allocated to size. + virtual memory_ptr set(size_t offset, size_t size, + uint8_t backfill) NOEXCEPT = 0; + /// Get remap-protected r/w access to start/offset of memory map (or null). /// Pointer is constrained to starting write within full capacity. virtual memory_ptr get_capacity(size_t offset=zero) const NOEXCEPT = 0; @@ -96,9 +100,10 @@ class storage /// Pointer is constrained to starting write within full capacity. virtual memory::iterator get_raw(size_t offset=zero) const NOEXCEPT = 0; - /// Get remap-protected r/w access to offset (or null) allocated to size. - virtual memory_ptr set(size_t offset, size_t size, - uint8_t backfill) NOEXCEPT = 0; + /// Get unprotected r/w access to start/offset of memory map (or null). + /// Pointer is constrained to starting write within full capacity. + virtual memory::iterator get_at_raw(size_t column, + size_t offset=zero) const NOEXCEPT = 0; /// Get remap-protected r/w access to start/offset of memory map (or null). /// Pointer is constrained to starting write within logical allocation. @@ -108,6 +113,15 @@ class storage /// Pointer is constrained to starting write within logical allocation. virtual memory_ptr get_at(size_t column, size_t offset=zero) const NOEXCEPT = 0; + + /// Get remap-protected r/w access to start/offset of memory map (or null). + /// Pointer is constrained to starting write within logical allocation. + virtual memory get1(size_t offset=zero) const NOEXCEPT = 0; + + /// Same as get() but within specified column (or null for invalid column). + /// Pointer is constrained to starting write within logical allocation. + virtual memory get_at1(size_t column, + size_t offset=zero) const NOEXCEPT = 0; }; } // namespace database diff --git a/include/bitcoin/database/memory/memory.hpp b/include/bitcoin/database/memory/memory.hpp index 0d8002f60..8742fba66 100644 --- a/include/bitcoin/database/memory/memory.hpp +++ b/include/bitcoin/database/memory/memory.hpp @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/include/bitcoin/database/memory/mmap.hpp b/include/bitcoin/database/memory/mmap.hpp index c425b981a..b78d6ec33 100644 --- a/include/bitcoin/database/memory/mmap.hpp +++ b/include/bitcoin/database/memory/mmap.hpp @@ -27,7 +27,6 @@ #include #include #include -#include #include namespace libbitcoin { @@ -132,15 +131,19 @@ class mmap /// Increase logical by specified rows/bytes, return row of first (or eof). size_t allocate(size_t count) NOEXCEPT override; + /// Remap-protected r/w access to offset (or null) allocated to size. + memory_ptr set(size_t offset, size_t size, + uint8_t backfill) NOEXCEPT override; + /// Remap-protected r/w access to start/offset (or null), within capacity. memory_ptr get_capacity(size_t offset=zero) const NOEXCEPT override; /// Unprotected r/w access to start/offset (or null), within logical. memory::iterator get_raw(size_t offset=zero) const NOEXCEPT override; - /// Remap-protected r/w access to offset (or null) allocated to size. - memory_ptr set(size_t offset, size_t size, - uint8_t backfill) NOEXCEPT override; + /// Unprotected r/w access to start/offset (or null), within logical. + memory::iterator get_at_raw(size_t column, + size_t offset=zero) const NOEXCEPT override; /// Remap-protected r/w access to start/offset (or null), within logical. memory_ptr get(size_t offset=zero) const NOEXCEPT override; @@ -149,6 +152,12 @@ class mmap memory_ptr get_at(size_t column, size_t offset=zero) const NOEXCEPT override; + /// Remap-protected r/w access to start/offset (or null), within logical. + memory get1(size_t offset=zero) const NOEXCEPT override; + + /// Same as get() but within specified column (or null for invalid column). + memory get_at1(size_t column, size_t offset=zero) const NOEXCEPT override; + protected: template static constexpr size_t to_width(size_t offset) NOEXCEPT @@ -174,7 +183,6 @@ class mmap private: static constexpr auto fail = -1; - using access = accessor; using sequence = std::make_index_sequence; // mman dispatch, not thread safe. diff --git a/include/bitcoin/database/memory/reader.hpp b/include/bitcoin/database/memory/reader.hpp index 97e31caac..351e062dc 100644 --- a/include/bitcoin/database/memory/reader.hpp +++ b/include/bitcoin/database/memory/reader.hpp @@ -20,7 +20,7 @@ #define LIBBITCOIN_DATABASE_MEMORY_READER_HPP #include -#include +#include namespace libbitcoin { namespace database { diff --git a/include/bitcoin/database/memory/streamers.hpp b/include/bitcoin/database/memory/streamers.hpp index f64868937..652a4c01f 100644 --- a/include/bitcoin/database/memory/streamers.hpp +++ b/include/bitcoin/database/memory/streamers.hpp @@ -20,7 +20,7 @@ #define LIBBITCOIN_DATABASE_MEMORY_STREAMERS_HPP #include -#include +#include namespace libbitcoin { namespace database { diff --git a/include/bitcoin/database/primitives/column.hpp b/include/bitcoin/database/primitives/column.hpp index 09b6e777f..748c53987 100644 --- a/include/bitcoin/database/primitives/column.hpp +++ b/include/bitcoin/database/primitives/column.hpp @@ -20,6 +20,7 @@ #define LIBBITCOIN_DATABASE_PRIMITIVES_COLUMN_HPP #include +#include namespace libbitcoin { namespace database { @@ -36,7 +37,7 @@ class column { } - INLINE memory_ptr get_memory() const NOEXCEPT + INLINE memory get_memory() const NOEXCEPT { return table_.template get_memory(); } @@ -48,7 +49,7 @@ class column } template - static INLINE bool get(const memory_ptr& ptr, const link& record, + static INLINE bool get(const memory& ptr, const link& record, Element& element) NOEXCEPT { return Table::template get(ptr, record, element); @@ -61,7 +62,7 @@ class column } template - INLINE bool put(const memory_ptr& ptr, const Element& element) NOEXCEPT + INLINE bool put(const memory& ptr, const Element& element) NOEXCEPT { return table_.template put(ptr, element); } diff --git a/include/bitcoin/database/primitives/manager.hpp b/include/bitcoin/database/primitives/manager.hpp index 12da24fc1..6f9cbbeb0 100644 --- a/include/bitcoin/database/primitives/manager.hpp +++ b/include/bitcoin/database/primitives/manager.hpp @@ -45,11 +45,17 @@ class managers /// Return memory object for column full map (null only if oom or unloaded). template inline memory_ptr get() const NOEXCEPT; + template + inline memory get1() const NOEXCEPT; - /// Return memory object for column record at position (null possible). + /// Return memory object for column record (null only if oom or unloaded). /// Pointer is constrained to starting write within logical allocation. template inline memory_ptr get(const Link& link) const NOEXCEPT; + template + inline memory get1(const Link& link) const NOEXCEPT; + template + inline memory::iterator get_raw1(const Link& link) const NOEXCEPT; /// Return memory object (limited to AoS) within capacity. template = true> diff --git a/include/bitcoin/database/primitives/nomaps.hpp b/include/bitcoin/database/primitives/nomaps.hpp index 5690f8ce0..f68b2b25f 100644 --- a/include/bitcoin/database/primitives/nomaps.hpp +++ b/include/bitcoin/database/primitives/nomaps.hpp @@ -72,30 +72,34 @@ class nomaps /// Query interface (columnar). /// ----------------------------------------------------------------------- + /// Must hold as remap guard across puts(). + memory guard() const NOEXCEPT; + /// Column base ptr for batch processing (holds shared lock on body remap). template - memory_ptr get_memory() const NOEXCEPT; + memory get_memory() const NOEXCEPT; /// Get element from column at link using column base ptr (deserialize). template - static bool get(const memory_ptr& ptr, const Link& link, + static bool get(const memory& ptr, const Link& link, Element& element) NOEXCEPT; /// Get element from column at link. template bool get(const Link& link, Element& element) const NOEXCEPT; - /// Put previously allocated element to column at link. + /// Put previously allocated element to column at link (guard required). template bool put(const Link& link, const Element& element) NOEXCEPT; - template - bool put(const memory_ptr& ptr, const Element& element) NOEXCEPT; protected: using head = database::nohead; using body = database::managers, Columns::width...>; + template + bool put(memory::iterator it, const Element& element) NOEXCEPT; + // Thread safe (index/top/push). // Not thread safe (create/open/close/backup/restore). head head_; diff --git a/test/memory/accessor.cpp b/test/memory/accessor.cpp index 6293fd30e..4bcfd1fa5 100644 --- a/test/memory/accessor.cpp +++ b/test/memory/accessor.cpp @@ -22,10 +22,11 @@ BOOST_AUTO_TEST_SUITE(accessor_tests) using namespace system; -BOOST_AUTO_TEST_CASE(accessor__construct_shared_mutex__unassigned__nulls) +BOOST_AUTO_TEST_CASE(accessor__construct_shared_mutex__unassigned__invalid_nulls) { std::shared_mutex mutex; accessor instance(mutex); + BOOST_REQUIRE(!instance); BOOST_REQUIRE(is_null(instance.data())); BOOST_REQUIRE(is_null(instance.begin())); BOOST_REQUIRE(is_null(instance.end())); @@ -34,7 +35,7 @@ BOOST_AUTO_TEST_CASE(accessor__construct_shared_mutex__unassigned__nulls) BOOST_AUTO_TEST_CASE(accessor__destruct__shared_lock__released) { std::shared_mutex mutex; - auto access = std::make_shared>(mutex); + auto access = std::make_shared(mutex); BOOST_REQUIRE(!mutex.try_lock()); access.reset(); BOOST_REQUIRE(mutex.try_lock()); diff --git a/test/mocks/chunk_storage.hpp b/test/mocks/chunk_storage.hpp index b98d4fa53..55a97ce59 100644 --- a/test/mocks/chunk_storage.hpp +++ b/test/mocks/chunk_storage.hpp @@ -240,20 +240,6 @@ class chunk_storages // access // ------------------------------------------------------------------------ - memory_ptr get_capacity(size_t offset=zero) const NOEXCEPT override - { - using namespace system; - auto& buffer = at(zero); - const auto ptr = emplace_shared>(map_mutex_); - ptr->assign(get_raw(offset), std::next(buffer.data(), buffer.size())); - return ptr; - } - - memory::iterator get_raw(size_t offset=zero) const NOEXCEPT override - { - return std::next(at(zero).data(), offset); - } - memory_ptr set(size_t offset, size_t size, uint8_t backfill) NOEXCEPT override { { @@ -275,6 +261,26 @@ class chunk_storages return get(offset); } + memory_ptr get_capacity(size_t offset=zero) const NOEXCEPT override + { + using namespace system; + auto& buffer = at(zero); + const auto ptr = emplace_shared(map_mutex_); + ptr->assign(get_raw(offset), std::next(buffer.data(), buffer.size())); + return ptr; + } + + memory::iterator get_raw(size_t offset=zero) const NOEXCEPT override + { + return std::next(at(zero).data(), offset); + } + + memory::iterator get_at_raw(size_t column, + size_t offset=zero) const NOEXCEPT override + { + return std::next(at(column).data(), offset); + } + memory_ptr get(size_t offset=zero) const NOEXCEPT override { return get_at(zero, offset); @@ -285,11 +291,26 @@ class chunk_storages using namespace system; auto data = at(column).data(); const auto allocated = size() * widths.at(column); - const auto ptr = emplace_shared>(map_mutex_); + const auto ptr = emplace_shared(map_mutex_); ptr->assign(std::next(data, offset), std::next(data, allocated)); return ptr; } + memory get1(size_t offset=zero) const NOEXCEPT override + { + return get_at1(zero, offset); + } + + memory get_at1(size_t column, size_t offset=zero) const NOEXCEPT override + { + using namespace system; + auto data = at(column).data(); + const auto allocated = size() * widths.at(column); + accessor out(map_mutex_); + out.assign(std::next(data, offset), std::next(data, allocated)); + return out; + } + // This is protected by mutex. mutable std::array buffers_{};