diff --git a/Makefile.am b/Makefile.am
index 40c6b282..99c0ca6a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,8 +80,11 @@ check_PROGRAMS = test/libbitcoin-node-test
test_libbitcoin_node_test_CPPFLAGS = -I${srcdir}/include ${bitcoin_database_BUILD_CPPFLAGS} ${bitcoin_network_BUILD_CPPFLAGS}
test_libbitcoin_node_test_LDADD = src/libbitcoin-node.la ${boost_unit_test_framework_LIBS} ${bitcoin_database_LIBS} ${bitcoin_network_LIBS}
test_libbitcoin_node_test_SOURCES = \
+ test/block_arena.cpp \
+ test/block_memory.cpp \
test/configuration.cpp \
test/error.cpp \
+ test/full_node.cpp \
test/main.cpp \
test/node.cpp \
test/settings.cpp \
diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt
index c73cfaee..62d3b604 100644
--- a/builds/cmake/CMakeLists.txt
+++ b/builds/cmake/CMakeLists.txt
@@ -320,8 +320,11 @@ target_link_libraries( ${CANONICAL_LIB_NAME}
#------------------------------------------------------------------------------
if (with-tests)
add_executable( libbitcoin-node-test
+ "../../test/block_arena.cpp"
+ "../../test/block_memory.cpp"
"../../test/configuration.cpp"
"../../test/error.cpp"
+ "../../test/full_node.cpp"
"../../test/main.cpp"
"../../test/node.cpp"
"../../test/settings.cpp"
diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj
index a3ac4147..95c230f1 100644
--- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj
@@ -70,6 +70,8 @@
+
+
@@ -81,6 +83,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters
index 0fc4ebb1..c05046ef 100644
--- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters
@@ -21,6 +21,12 @@
+
+ src
+
+
+ src
+
src\chasers
@@ -54,6 +60,9 @@
src
+
+ src
+
src
diff --git a/include/bitcoin/node/block_arena.hpp b/include/bitcoin/node/block_arena.hpp
index 157aaf0b..1b089049 100644
--- a/include/bitcoin/node/block_arena.hpp
+++ b/include/bitcoin/node/block_arena.hpp
@@ -27,7 +27,7 @@ namespace libbitcoin {
namespace node {
/// Thread UNSAFE detachable linked-linear memory arena.
-class BCN_API block_arena final
+class BCN_API block_arena
: public arena
{
public:
@@ -35,7 +35,7 @@ class BCN_API block_arena final
block_arena(size_t multiple) NOEXCEPT;
block_arena(block_arena&& other) NOEXCEPT;
- ~block_arena() NOEXCEPT;
+ virtual ~block_arena() NOEXCEPT;
block_arena& operator=(block_arena&& other) NOEXCEPT;
@@ -49,6 +49,33 @@ class BCN_API block_arena final
void release(void* address) NOEXCEPT override;
protected:
+ /// Determine alignment offset.
+ static constexpr size_t to_aligned(size_t value, size_t align) NOEXCEPT
+ {
+ using namespace system;
+ BC_ASSERT_MSG(is_nonzero(align), "align zero");
+ BC_ASSERT_MSG(!is_add_overflow(value, sub1(align)), "overflow");
+ BC_ASSERT_MSG(power2(floored_log2(align)) == align, "align power");
+ BC_ASSERT_MSG(align <= alignof(std::max_align_t), "align overflow");
+ return (value + sub1(align)) & ~sub1(align);
+ }
+
+ /// Malloc throws if memory is not allocated.
+ virtual INLINE void* malloc(size_t bytes) THROWS
+ {
+ BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
+ return std::malloc(bytes);
+ BC_POP_WARNING()
+ }
+
+ /// Free does not throw, behavior is undefined if address is incorrect.
+ virtual INLINE void free(void* address) NOEXCEPT
+ {
+ BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
+ std::free(address);
+ BC_POP_WARNING()
+ }
+
/// Link a memory chunk to the allocated stack.
void push(size_t minimum=zero) THROWS;
@@ -79,17 +106,6 @@ class BCN_API block_arena final
return system::floored_subtract(size_, offset_);
}
-private:
- constexpr size_t to_aligned(size_t value, size_t align) NOEXCEPT
- {
- using namespace system;
- BC_ASSERT_MSG(is_nonzero(align), "align zero");
- BC_ASSERT_MSG(!is_add_overflow(value, sub1(align)), "overflow");
- BC_ASSERT_MSG(power2(floored_log2(align)) == align, "align power");
- BC_ASSERT_MSG(align <= alignof(std::max_align_t), "align overflow");
- return (value + sub1(align)) & ~sub1(align);
- }
-
void* do_allocate(size_t bytes, size_t align) THROWS override;
void do_deallocate(void* ptr, size_t bytes, size_t align) NOEXCEPT override;
bool do_is_equal(const arena& other) const NOEXCEPT override;
@@ -100,7 +116,6 @@ class BCN_API block_arena final
size_t offset_;
size_t total_;
size_t size_;
-
};
} // namespace node
diff --git a/include/bitcoin/node/block_memory.hpp b/include/bitcoin/node/block_memory.hpp
index 310d6e43..47106251 100644
--- a/include/bitcoin/node/block_memory.hpp
+++ b/include/bitcoin/node/block_memory.hpp
@@ -28,7 +28,7 @@ namespace libbitcoin {
namespace node {
/// Thread SAFE linked-linear arena allocator.
-class BCN_API block_memory final
+class BCN_API block_memory
: public network::memory
{
public:
@@ -41,9 +41,9 @@ class BCN_API block_memory final
/// Each thread obtains an arena.
arena* get_arena() NOEXCEPT override;
-private:
+protected:
// This is thread safe.
- std::atomic_size_t count_{};
+ std::atomic_size_t count_{ zero };
// This is protected by constructor init and thread_local indexation.
std::vector arenas_{};
diff --git a/src/block_arena.cpp b/src/block_arena.cpp
index 1cc246d7..8212c604 100644
--- a/src/block_arena.cpp
+++ b/src/block_arena.cpp
@@ -52,7 +52,6 @@ block_arena::block_arena(block_arena&& other) NOEXCEPT
block_arena::~block_arena() NOEXCEPT
{
- release(memory_map_);
}
block_arena& block_arena::operator=(block_arena&& other) NOEXCEPT
@@ -96,11 +95,7 @@ void block_arena::release(void* address) NOEXCEPT
while (!is_null(address))
{
const auto link = get_link(pointer_cast(address));
-
- BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
- std::free(address);
- BC_POP_WARNING()
-
+ free(address);
address = link;
}
}
@@ -115,10 +110,7 @@ void block_arena::push(size_t minimum) THROWS
// Ensure next allocation accomodates link plus current request.
BC_ASSERT(!is_add_overflow(minimum, link_size));
size_ = std::max(size_, minimum + link_size);
-
- BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
- const auto map = pointer_cast(std::malloc(size_));
- BC_POP_WARNING()
+ const auto map = pointer_cast(malloc(size_));
if (is_null(map))
throw allocation_exception{};
diff --git a/test/block_arena.cpp b/test/block_arena.cpp
new file mode 100644
index 00000000..a3c51e83
--- /dev/null
+++ b/test/block_arena.cpp
@@ -0,0 +1,320 @@
+/**
+ * Copyright (c) 2011-2024 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 .
+ */
+#include "test.hpp"
+
+BOOST_AUTO_TEST_SUITE(block_arena_tests)
+
+using namespace system;
+
+class accessor
+ : public block_arena
+{
+public:
+ // public
+
+ using block_arena::block_arena;
+
+ const uint8_t* get_memory_map() const NOEXCEPT
+ {
+ return memory_map_;
+ }
+
+ void set_memory_map(uint8_t* map) NOEXCEPT
+ {
+ memory_map_ = map;
+ }
+
+ size_t get_multiple() const NOEXCEPT
+ {
+ return multiple_;
+ }
+
+ void set_multiple(size_t multiple) NOEXCEPT
+ {
+ multiple_ = multiple;
+ }
+
+ size_t get_offset() const NOEXCEPT
+ {
+ return offset_;
+ }
+
+ void set_offset(size_t offset) NOEXCEPT
+ {
+ offset_ = offset;
+ }
+
+ size_t get_total() const NOEXCEPT
+ {
+ return total_;
+ }
+
+ void set_total(size_t total) NOEXCEPT
+ {
+ total_ = total;
+ }
+
+ size_t get_size() const NOEXCEPT
+ {
+ return size_;
+ }
+
+ void set_size(size_t size) NOEXCEPT
+ {
+ size_ = size;
+ }
+
+ void release(void* address) NOEXCEPT override
+ {
+ block_arena::release(address);
+ }
+
+ // protected
+
+ static constexpr size_t to_aligned_(size_t value, size_t align) NOEXCEPT
+ {
+ return to_aligned(value, align);
+ }
+
+ void* malloc(size_t bytes) THROWS override
+ {
+ stack.emplace_back(bytes);
+ return stack.back().data();
+ }
+
+ void free(void* address) NOEXCEPT override
+ {
+ freed.push_back(address);
+ }
+
+ void push_(size_t minimum=zero) THROWS
+ {
+ push(minimum);
+ }
+
+ void set_link_(uint8_t* next_address) NOEXCEPT
+ {
+ set_link(next_address);
+ }
+
+ void* get_link_(uint8_t* address) const NOEXCEPT
+ {
+ return get_link(address);
+ }
+
+ size_t capacity_() const NOEXCEPT
+ {
+ return capacity();
+ }
+
+ // These can be directly invoked via public methods.
+ //void* do_allocate(size_t bytes, size_t align) THROWS override;
+ //void do_deallocate(void* ptr, size_t bytes, size_t align) NOEXCEPT override;
+ //bool do_is_equal(const arena& other) const NOEXCEPT override;
+
+ data_stack stack{};
+ std::vector freed{};
+};
+
+// construct
+
+BOOST_AUTO_TEST_CASE(block_arena__construct__zero__sets_zero)
+{
+ constexpr auto multiple = zero;
+ const accessor instance{ multiple };
+ BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr);
+ BOOST_REQUIRE_EQUAL(instance.get_multiple(), multiple);
+ BOOST_REQUIRE_EQUAL(instance.get_offset(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_total(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_size(), zero);
+}
+
+BOOST_AUTO_TEST_CASE(block_arena__construct__value__sets_multiple)
+{
+ constexpr auto multiple = 42u;
+ const accessor instance{ multiple };
+ BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr);
+ BOOST_REQUIRE_EQUAL(instance.get_multiple(), multiple);
+ BOOST_REQUIRE_EQUAL(instance.get_offset(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_total(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_size(), zero);
+}
+
+BOOST_AUTO_TEST_CASE(block_arena__move_construct__always__nulls_memory_map)
+{
+ constexpr auto multiple = 42u;
+ accessor instance{ multiple };
+ system::data_chunk value{ 0x00 };
+ auto address = value.data();
+ instance.set_memory_map(address);
+ BOOST_REQUIRE_EQUAL(instance.get_memory_map(), address);
+
+ const accessor copy{ std::move(instance) };
+ BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr);
+ BOOST_REQUIRE_EQUAL(copy.get_memory_map(), address);
+ BOOST_REQUIRE_EQUAL(copy.get_multiple(), multiple);
+ BOOST_REQUIRE_EQUAL(copy.get_offset(), zero);
+ BOOST_REQUIRE_EQUAL(copy.get_total(), zero);
+ BOOST_REQUIRE_EQUAL(copy.get_size(), zero);
+}
+
+// assign
+
+BOOST_AUTO_TEST_CASE(block_arena__assign__always__nulls_memory_map)
+{
+ constexpr auto multiple = 42u;
+ accessor instance{ multiple };
+ system::data_chunk value{ 0x00 };
+ auto address = value.data();
+ instance.set_memory_map(address);
+ BOOST_REQUIRE_EQUAL(instance.get_memory_map(), address);
+
+ const accessor copy = std::move(instance);
+ BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr);
+ BOOST_REQUIRE_EQUAL(copy.get_memory_map(), address);
+ BOOST_REQUIRE_EQUAL(copy.get_multiple(), multiple);
+ BOOST_REQUIRE_EQUAL(copy.get_offset(), zero);
+ BOOST_REQUIRE_EQUAL(copy.get_total(), zero);
+ BOOST_REQUIRE_EQUAL(copy.get_size(), zero);
+}
+
+// start
+
+BOOST_AUTO_TEST_CASE(block_arena__start__multiple_overflow__throws_allocation_exception)
+{
+ accessor instance{ two };
+
+ BC_PUSH_WARNING(DISCARDING_NON_DISCARDABLE)
+ BOOST_REQUIRE_THROW(instance.start(max_size_t), allocation_exception);
+ BC_POP_WARNING()
+}
+
+BOOST_AUTO_TEST_CASE(block_arena__start__zero__minimal_allocation)
+{
+ constexpr auto size = zero;
+ constexpr auto multiple = 42u;
+ accessor instance{ multiple };
+ const auto memory = instance.start(size);
+ BOOST_REQUIRE_NE(memory, nullptr);
+ BOOST_REQUIRE_LT(size, sizeof(void*));
+ BOOST_REQUIRE_EQUAL(instance.stack.size(), one);
+ BOOST_REQUIRE_EQUAL(instance.stack.front().size(), sizeof(void*));
+}
+
+BOOST_AUTO_TEST_CASE(block_arena__start__at_least_pointer_size__expected)
+{
+ constexpr auto size = 42u;
+ constexpr auto multiple = 10u;
+ accessor instance{ multiple };
+ const auto memory = instance.start(size);
+ BOOST_REQUIRE_NE(memory, nullptr);
+ BOOST_REQUIRE_GE(size, sizeof(void*));
+ BOOST_REQUIRE_EQUAL(instance.stack.size(), one);
+ BOOST_REQUIRE_EQUAL(instance.stack.front().size(), size * multiple);
+ BOOST_REQUIRE_EQUAL(instance.stack.front().data(), instance.get_memory_map());
+}
+
+// detach
+
+BOOST_AUTO_TEST_CASE(block_arena__detach__unstarted__zero)
+{
+ accessor instance{ 42 };
+ const auto size = instance.detach();
+ BOOST_REQUIRE_EQUAL(size, zero);
+}
+
+// release
+
+BOOST_AUTO_TEST_CASE(block_arena__release__nullptr__nop)
+{
+ accessor instance{ 42 };
+ instance.release(nullptr);
+}
+
+// to_aligned
+
+BOOST_AUTO_TEST_CASE(block_arena__to_aligned__zero_one__zero)
+{
+ constexpr auto aligned = accessor::to_aligned_(0, 1);
+ BOOST_REQUIRE_EQUAL(aligned, zero);
+}
+
+// push
+
+BOOST_AUTO_TEST_CASE(block_arena__push__)
+{
+ accessor instance{ 42 };
+ instance.push_();
+}
+
+// set_link
+
+BOOST_AUTO_TEST_CASE(block_arena__set_link__nullptr__nop)
+{
+ accessor instance{ 42 };
+ instance.set_link_(nullptr);
+}
+
+// get_link
+
+BOOST_AUTO_TEST_CASE(block_arena__get_link__unstarted__zero_filled)
+{
+ accessor instance{ 42 };
+ instance.stack.emplace_back(sizeof(void*));
+ const auto link = instance.get_link_(instance.stack.front().data());
+ BOOST_REQUIRE_EQUAL(link, nullptr);
+}
+
+// capacity
+
+BOOST_AUTO_TEST_CASE(block_arena__capacity__unstarted__zero)
+{
+ accessor instance{ 42 };
+ const auto capacity = instance.capacity_();
+ BOOST_REQUIRE_EQUAL(capacity, zero);
+}
+
+// do_allocate/do_deallocate
+
+BOOST_AUTO_TEST_CASE(block_arena__do_allocate__do_deallocate__expected)
+{
+ accessor instance{ 42 };
+ const auto block = instance.start(0);
+ BOOST_REQUIRE_NE(block, nullptr);
+
+ const auto memory = instance.allocate(24, 4);
+ instance.deallocate(memory, 24, 4);
+}
+
+// do_is_equal
+
+BOOST_AUTO_TEST_CASE(block_arena__do_is_equal__equal__true)
+{
+ accessor instance{ 42 };
+ BOOST_REQUIRE(instance.is_equal(instance));
+}
+
+BOOST_AUTO_TEST_CASE(block_arena__do_is_equal__unequal__false)
+{
+ accessor instance1{ 42 };
+ accessor instance2{ 42 };
+ BOOST_REQUIRE(!instance1.is_equal(instance2));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/block_memory.cpp b/test/block_memory.cpp
new file mode 100644
index 00000000..e37bb38d
--- /dev/null
+++ b/test/block_memory.cpp
@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2011-2024 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 .
+ */
+#include "test.hpp"
+
+#include
+
+BOOST_AUTO_TEST_SUITE(block_memory_tests)
+
+class accessor
+ : public block_memory
+{
+public:
+ using block_memory::block_memory;
+
+ size_t get_count() const NOEXCEPT
+ {
+ return count_;
+ }
+
+ size_t get_size() const NOEXCEPT
+ {
+ return arenas_.size();
+ }
+
+ arena* get_arena_at(size_t index) NOEXCEPT
+ {
+ return &arenas_.at(index);
+ }
+};
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__no_multiple_no_threads__default_arena)
+{
+ constexpr size_t multiple = 0;
+ constexpr size_t threads = 0;
+ accessor instance{ multiple, threads };
+ BOOST_REQUIRE_EQUAL(instance.get_size(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_arena(), default_arena::get());
+}
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__no_threads__default_arena)
+{
+ constexpr size_t multiple = 42;
+ constexpr size_t threads = 0;
+ accessor instance{ multiple, threads };
+ BOOST_REQUIRE_EQUAL(instance.get_size(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_arena(), default_arena::get());
+}
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__no_multiple__default_arena)
+{
+ constexpr size_t multiple = 0;
+ constexpr size_t threads = 1;
+ accessor instance{ multiple, threads };
+ BOOST_REQUIRE_EQUAL(instance.get_size(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+ BOOST_REQUIRE_EQUAL(instance.get_arena(), default_arena::get());
+}
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__multiple_one_thread__not_default_arena)
+{
+ constexpr size_t multiple = 42;
+ constexpr size_t threads = 1;
+ accessor instance{ multiple, threads };
+ BOOST_REQUIRE_EQUAL(instance.get_size(), one);
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+ BOOST_REQUIRE_NE(instance.get_arena(), default_arena::get());
+}
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__multiple_threads__count_unincremented)
+{
+ constexpr size_t multiple = 42;
+ constexpr size_t threads = 2;
+ accessor instance{ multiple, threads };
+ BOOST_REQUIRE_EQUAL(instance.get_size(), two);
+
+ // On any given thread count must not change.
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+ BOOST_REQUIRE_NE(instance.get_arena(), default_arena::get());
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+ BOOST_REQUIRE_NE(instance.get_arena(), default_arena::get());
+ BOOST_REQUIRE_EQUAL(instance.get_count(), zero);
+}
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__two_threads__independent_not_default_arenas)
+{
+ // non-zero multiple ensures block_arenas but value is otherwise unimportant.
+ constexpr size_t multiple = 42;
+ constexpr size_t threads = 2;
+ accessor instance{ multiple, threads };
+ size_t count1{};
+ size_t count2{};
+ void* arena1{};
+ void* arena2{};
+
+ std::thread thread1([&]() NOEXCEPT
+ {
+ count1 = instance.get_count();
+ arena1 = instance.get_arena();
+ });
+
+ std::thread thread2([&]() NOEXCEPT
+ {
+ count2 = instance.get_count();
+ arena2 = instance.get_arena();
+ });
+
+ thread1.join();
+ thread2.join();
+
+ BOOST_REQUIRE_NE(arena2, default_arena::get());
+ BOOST_REQUIRE_NE(arena1, default_arena::get());
+ BOOST_REQUIRE_NE(arena1, arena2);
+ BOOST_REQUIRE_NE(count1, count2);
+}
+
+BOOST_AUTO_TEST_CASE(block_memory__get_arena__overflow_threads__default_arena)
+{
+ constexpr size_t multiple = 42;
+ constexpr size_t threads = 2;
+ accessor instance{ multiple, threads };
+ size_t count1a{};
+ size_t count1b{};
+ size_t count2a{};
+ size_t count2b{};
+ size_t count3a{};
+ size_t count3b{};
+ void* arena1{};
+ void* arena2{};
+ void* arena3{};
+
+ // order is required to ensure third is the overflow.
+ std::thread thread1([&]() NOEXCEPT
+ {
+ count1a = instance.get_count();
+ arena1 = instance.get_arena();
+ count1b = instance.get_count();
+
+ std::thread thread2([&]() NOEXCEPT
+ {
+ count2a = instance.get_count();
+ arena2 = instance.get_arena();
+ count2b = instance.get_count();
+
+ std::thread thread3([&]() NOEXCEPT
+ {
+ count3a = instance.get_count();
+ arena3 = instance.get_arena();
+ count3b = instance.get_count();
+ });
+
+ thread3.join();
+ });
+
+ thread2.join();
+ });
+
+ thread1.join();
+
+ // Arenas are ordered by thread order above.
+ BOOST_REQUIRE_EQUAL(arena1, instance.get_arena_at(0));
+ BOOST_REQUIRE_EQUAL(arena2, instance.get_arena_at(1));
+ BOOST_REQUIRE_NE(arena1, arena2);
+
+ // Index overflows to default arena.
+ BOOST_REQUIRE_EQUAL(arena3, default_arena::get());
+
+ // Count reflects total, not the index of the thread.
+ BOOST_REQUIRE_EQUAL(count1a, 0u);
+ BOOST_REQUIRE_EQUAL(count2a, 1u);
+ BOOST_REQUIRE_EQUAL(count3a, 2u);
+ BOOST_REQUIRE_EQUAL(count1b, 1u);
+ BOOST_REQUIRE_EQUAL(count2b, 2u);
+ BOOST_REQUIRE_EQUAL(count3b, 3u);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/full_node.cpp b/test/full_node.cpp
new file mode 100644
index 00000000..c7a51955
--- /dev/null
+++ b/test/full_node.cpp
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2011-2024 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 .
+ */
+#include "test.hpp"
+
+BOOST_AUTO_TEST_SUITE(full_node_tests)
+
+BOOST_AUTO_TEST_CASE(full_node_test)
+{
+ BOOST_REQUIRE(true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()