diff --git a/include/libpmr/block_pool.h b/include/libpmr/block_pool.h new file mode 100644 index 0000000..f38ecbb --- /dev/null +++ b/include/libpmr/block_pool.h @@ -0,0 +1,131 @@ +/** + * \file libpmr/block_pool.h + * \author mutouyun (orz@orzz.org) + * \brief Fixed-length memory block pool. + * \date 2023-11-18 + */ +#pragma once + +#include +#include +#include +#include + +#include "libimp/byte.h" +#include "libconcur/intrusive_stack.h" + +#include "libpmr/def.h" +#include "libpmr/memory_resource.h" + +LIBPMR_NAMESPACE_BEG_ + +/** + * \brief The block type. + * \tparam BlockSize specifies the memory block size +*/ +template +union block { + block *next; + alignas(std::max_align_t) std::array<::LIBIMP::byte, BlockSize> storage; +}; + +/** + * \brief A fixed-length memory block central cache pool. + * \tparam BlockSize specifies the memory block size + */ +template +class central_cache_pool { + + /// \brief The block type. + using block_t = block; + + /// \brief The central cache stack. + ::LIBCONCUR::intrusive_stack cache_; + ::LIBCONCUR::intrusive_stack aqueired_; + + central_cache_pool() noexcept = default; + +public: + block_t *aqueire() noexcept { + auto *n = cache_.pop(); + if (n != nullptr) { + aqueired_.push(n); + return n->value; + } + auto *blocks = new(std::nothrow) std::array; + if (blocks == nullptr) { + return nullptr; + } + for (std::size_t i = 0; i < block_pool_expansion - 1; ++i) { + (*blocks)[i].next = &(*blocks)[i + 1]; + } + blocks->back().next = nullptr; + return blocks->data(); + } + + void release(block_t *p) noexcept { + if (p == nullptr) return; + auto *a = aqueired_.pop(); + if (a == nullptr) { + a = new(std::nothrow) ::LIBCONCUR::intrusive_stack::node; + if (a == nullptr) return; + } + a->value = p; + cache_.push(a); + } + + /// \brief Get the singleton instance. + static central_cache_pool &instance() noexcept { + static central_cache_pool pool; + return pool; + } +}; + +/** + * \brief Fixed-length memory block pool. + * \tparam BlockSize specifies the memory block size + */ +template +class block_pool { + + /// \brief The block type. + using block_t = block; + + /// \brief Expand the block pool when it is exhausted. + block_t *expand() noexcept { + return central_cache_pool::instance().aqueire(); + } + +public: + constexpr static std::size_t block_size = BlockSize; + + block_pool() noexcept : cursor_(expand()) {} + ~block_pool() noexcept { + central_cache_pool::instance().release(cursor_); + } + + block_pool(block_pool const &) = delete; + block_pool& operator=(block_pool const &) = delete; + + void *allocate() noexcept { + if (cursor_ == nullptr) { + cursor_ = expand(); + if (cursor_ == nullptr) return nullptr; + } + block_t *p = cursor_; + cursor_ = cursor_->next; + return p->storage.data(); + } + + void deallocate(void *p) noexcept { + if (p == nullptr) return; + block_t *b = static_cast(p); + b->next = cursor_; + cursor_ = b; + } + +private: + block_t *cursor_; +}; + +LIBPMR_NAMESPACE_END_ diff --git a/include/libpmr/def.h b/include/libpmr/def.h index ff4c753..b724aa9 100644 --- a/include/libpmr/def.h +++ b/include/libpmr/def.h @@ -14,4 +14,8 @@ LIBPMR_NAMESPACE_BEG_ /// \brief Constants. +enum : std::size_t { + block_pool_expansion = 64, +}; + LIBPMR_NAMESPACE_END_ diff --git a/test/pmr/test_pmr_block_pool.cpp b/test/pmr/test_pmr_block_pool.cpp new file mode 100644 index 0000000..7e8ec86 --- /dev/null +++ b/test/pmr/test_pmr_block_pool.cpp @@ -0,0 +1,7 @@ + +#include "gtest/gtest.h" + +#include "libpmr/block_pool.h" + +TEST(block_pool, construct) { +}