Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor block_arena. #678

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions include/bitcoin/node/block_arena.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,37 +40,46 @@ class BCN_API block_arena final
block_arena& operator=(block_arena&& other) NOEXCEPT;

/// Start an allocation of linked chunks.
void* start(size_t wire_size) THROWS override;
NODISCARD void* start(size_t wire_size) THROWS override;

/// Finalize allocation and reset allocator, return total allocation.
size_t detach() THROWS override;
size_t detach() NOEXCEPT override;

/// Release all chunks chained to the address.
void release(void* address) NOEXCEPT override;

protected:
struct record{ void* next; size_t size; };

/// Link a memory chunk to the allocated list.
void* link_new_chunk(size_t minimum=zero) THROWS;

/// Trim chunk to offset_, invalidates capacity.
void trim_to_offset() THROWS;
/// Link a memory chunk to the allocated stack.
void push(size_t minimum=zero) THROWS;

/// Close out chunk with link to next.
void set_record(uint8_t* next_address, size_t own_size) NOEXCEPT;
INLINE void set_link(uint8_t* next_address) NOEXCEPT
{
// Don't set previous when current is the first chunk.
if (!is_null(memory_map_))
{
BC_PUSH_WARNING(NO_REINTERPRET_CAST)
reinterpret_cast<void*&>(*memory_map_) = next_address;
BC_POP_WARNING()
}
}

/// Get size of address chunk and address of next chunk (or nullptr).
record get_record(uint8_t* address) const NOEXCEPT;
INLINE void* get_link(uint8_t* address) const NOEXCEPT
{
BC_ASSERT(!is_null(address));
BC_PUSH_WARNING(NO_REINTERPRET_CAST)
return reinterpret_cast<void*&>(*address);
BC_POP_WARNING()
}

/// Number of bytes remaining to be allocated.
size_t capacity() const NOEXCEPT;

/// Reset members (does not free).
size_t reset(size_t chunk_size=zero) NOEXCEPT;
INLINE size_t capacity() const NOEXCEPT
{
return system::floored_subtract(size_, offset_);
}

private:
static constexpr size_t record_size = sizeof(record);
constexpr size_t to_aligned(size_t value, size_t align) NOEXCEPT
{
using namespace system;
Expand Down
105 changes: 36 additions & 69 deletions src/block_arena.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ namespace node {

using namespace system;

BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
BC_PUSH_WARNING(NO_REINTERPRET_CAST)
BC_PUSH_WARNING(NO_POINTER_ARITHMETIC)

// construct/destruct/assign
// ----------------------------------------------------------------------------

Expand Down Expand Up @@ -80,93 +76,66 @@ void* block_arena::start(size_t wire_size) THROWS
if (is_multiply_overflow(wire_size, multiple_))
throw allocation_exception{};

release(memory_map_);
reset(multiple_ * wire_size);
return link_new_chunk();
size_ = wire_size * multiple_;
memory_map_ = nullptr;
offset_ = zero;
total_ = zero;
push();
return memory_map_;
}

size_t block_arena::detach() THROWS
size_t block_arena::detach() NOEXCEPT
{
trim_to_offset();
set_record(nullptr, offset_);
return reset();
set_link(nullptr);
memory_map_ = nullptr;
return total_ + offset_;
}

void block_arena::release(void* address) NOEXCEPT
{
while (!is_null(address))
{
const auto value = get_record(pointer_cast<uint8_t>(address));
std::free(address/*, value.size */);
address = value.next;
const auto link = get_link(pointer_cast<uint8_t>(address));

BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
std::free(address);
BC_POP_WARNING()

address = link;
}
}

// protected
// ----------------------------------------------------------------------------

void* block_arena::link_new_chunk(size_t minimum) THROWS
void block_arena::push(size_t minimum) THROWS
{
// Ensure next allocation accomodates record plus current request.
BC_ASSERT(!is_add_overflow(minimum, record_size));
size_ = std::max(size_, minimum + record_size);
static constexpr size_t link_size = sizeof(void*);

// Ensure next allocation accomodates link plus current request.
BC_ASSERT(!is_add_overflow(minimum, link_size));
size_ = std::max(size_, minimum + link_size);

// Allocate size to temporary.
BC_PUSH_WARNING(NO_MALLOC_OR_FREE)
const auto map = pointer_cast<uint8_t>(std::malloc(size_));
BC_POP_WARNING()

if (is_null(map))
throw allocation_exception{};

// Set previous chunk record pointer to new allocation and own size.
set_record(map, offset_);
offset_ = record_size;
return memory_map_ = map;
}

void block_arena::trim_to_offset() THROWS
{
// Memory map must not move. Move by realloc is allowed but not expected
// for truncation. If moves then this should drop into mmap/munmap/mremap.
////const auto map = std::realloc(memory_map_, offset_);
////if (map != memory_map_)
//// throw allocation_exception{};
}

void block_arena::set_record(uint8_t* next_address, size_t own_size) NOEXCEPT
{
// Don't set previous when current is the first chunk.
if (is_null(memory_map_))
return;

reinterpret_cast<record&>(*memory_map_) = { next_address, own_size };
total_ += own_size;
}

block_arena::record block_arena::get_record(uint8_t* address) const NOEXCEPT
{
return reinterpret_cast<const record&>(*address);
}

size_t block_arena::capacity() const NOEXCEPT
{
return floored_subtract(size_, offset_);
}

size_t block_arena::reset(size_t chunk_size) NOEXCEPT
{
// Chunk resets to nullptr/full with no total allocation.
const auto total = total_;
memory_map_ = nullptr;
offset_ = chunk_size;
size_ = chunk_size;
total_ = zero;
return total;
// Set previous chunk's link pointer to the new allocation.
set_link(map);
memory_map_ = map;
total_ += offset_;
offset_ = link_size;
}

// protected interface
// ----------------------------------------------------------------------------

void* block_arena::do_allocate(size_t bytes, size_t align) THROWS
{
BC_ASSERT(!is_null(memory_map_));
const auto aligned_offset = to_aligned(offset_, align);
const auto padding = aligned_offset - offset_;

Expand All @@ -175,14 +144,16 @@ void* block_arena::do_allocate(size_t bytes, size_t align) THROWS

if (allocation > capacity())
{
trim_to_offset();
link_new_chunk(allocation);
push(allocation);
return do_allocate(bytes, align);
}
else
{
offset_ += allocation;

BC_PUSH_WARNING(NO_POINTER_ARITHMETIC)
return memory_map_ + aligned_offset;
BC_POP_WARNING()
}
}

Expand All @@ -196,9 +167,5 @@ bool block_arena::do_is_equal(const arena& other) const NOEXCEPT
return &other == this;
}

BC_POP_WARNING()
BC_POP_WARNING()
BC_POP_WARNING()

} // namespace node
} // namespace libbitcoin
Loading