This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch dev-1-0-9 in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit d6fbf554738960766c0861baf2da962abb2ab80d Author: Alan M. Carroll <[email protected]> AuthorDate: Sun Nov 10 15:14:22 2019 -0600 Add FixedArena. --- swoc++/include/swoc/MemArena.h | 128 ++++++++++++++++++++++++++++------------- unit_tests/test_MemArena.cc | 31 ++++++++++ 2 files changed, 118 insertions(+), 41 deletions(-) diff --git a/swoc++/include/swoc/MemArena.h b/swoc++/include/swoc/MemArena.h index 7e35a1f..2b2e3b0 100644 --- a/swoc++/include/swoc/MemArena.h +++ b/swoc++/include/swoc/MemArena.h @@ -22,6 +22,7 @@ #include <mutex> #include <memory> #include <utility> +#include <new> #include "swoc/MemSpan.h" #include "swoc/Scalar.h" @@ -329,55 +330,89 @@ protected: // marks the last block to check. This keeps the set of blocks to check short. }; +/** Arena of a specific type on top of a @c MemArena. + * + * @tparam T Type in the arena. + * + * A pool of unused / free instances of @a T is kept for reuse. If none are available then a new + * instance is allocated from the arena. + */ +template <typename T> class FixedArena +{ + using self_type = FixedArena; ///< Self reference type. +protected: + /// Rebinding type for instances on the free list. + struct Item { + Item *_next; ///< Next item in the free list. + }; + + Item _list { nullptr }; ///< List of dead instances. + MemArena &_arena; ///< Memory source. + +public: + /** Construct a pool. + * + * @param arena The arena for memory. + */ + explicit FixedArena(MemArena &arena); + + /** Create a new instance. + * + * @tparam Args Constructor argument types. + * @param args Constructor arguments. + * @return A new instance of @a T. + */ + template <typename... Args> T *make(Args... args); + + /** Destroy an instance. + * + * @param t The instance to destroy. + * + * The instance is destructed and then put on the free list for re-use. + */ + void destroy(T *t); +}; // Implementation -inline auto -MemArena::Block::Linkage::next_ptr(Block *b) -> Block *& +inline auto MemArena::Block::Linkage::next_ptr(Block *b) -> Block *& { return b->_link._next; } -inline auto -MemArena::Block::Linkage::prev_ptr(Block *b) -> Block *& +inline auto MemArena::Block::Linkage::prev_ptr(Block *b) -> Block *& { return b->_link._prev; } inline MemArena::Block::Block(size_t n) : size(n) {} -inline char * -MemArena::Block::data() +inline char *MemArena::Block::data() { return reinterpret_cast<char *>(this + 1); } -inline const char * -MemArena::Block::data() const +inline const char *MemArena::Block::data() const { return reinterpret_cast<const char *>(this + 1); } -inline bool -MemArena::Block::contains(const void *ptr) const +inline bool MemArena::Block::contains(const void *ptr) const { const char *base = this->data(); return base <= ptr && ptr < base + size; } -inline size_t -MemArena::Block::remaining() const +inline size_t MemArena::Block::remaining() const { return size - allocated; } -inline bool -MemArena::Block::is_full() const +inline bool MemArena::Block::is_full() const { return this->remaining() < MIN_FREE_SPACE; } -inline MemSpan<void> -MemArena::Block::alloc(size_t n) +inline MemSpan<void> MemArena::Block::alloc(size_t n) { if (n > this->remaining()) { throw(std::invalid_argument{"MemArena::Block::alloc size is more than remaining."}); @@ -387,80 +422,91 @@ MemArena::Block::alloc(size_t n) return zret; } -template <typename T, typename... Args> -T * -MemArena::make(Args &&... args) +template <typename T, typename... Args> T *MemArena::make(Args &&... args) { return new (this->alloc(sizeof(T)).data()) T(std::forward<Args>(args)...); } inline MemArena::MemArena(size_t n) : _reserve_hint(n) {} -inline MemSpan<void> -MemArena::Block::remnant() +inline MemSpan<void> MemArena::Block::remnant() { return {this->data() + allocated, this->remaining()}; } -inline MemArena::Block & -MemArena::Block::discard() +inline MemArena::Block &MemArena::Block::discard() { allocated = 0; return *this; } -inline size_t -MemArena::size() const +inline size_t MemArena::size() const { return _active_allocated; } -inline size_t -MemArena::allocated_size() const +inline size_t MemArena::allocated_size() const { return _frozen_allocated + _active_allocated; } -inline size_t -MemArena::remaining() const +inline size_t MemArena::remaining() const { return _active.empty() ? 0 : _active.head()->remaining(); } -inline MemSpan<void> -MemArena::remnant() +inline MemSpan<void> MemArena::remnant() { return _active.empty() ? MemSpan<void>() : _active.head()->remnant(); } -inline size_t -MemArena::reserved_size() const +inline size_t MemArena::reserved_size() const { return _active_reserved + _frozen_reserved; } -inline auto -MemArena::begin() const -> const_iterator +inline auto MemArena::begin() const -> const_iterator { return _active.begin(); } -inline auto -MemArena::end() const -> const_iterator +inline auto MemArena::end() const -> const_iterator { return _active.end(); } -inline auto -MemArena::frozen_begin() const -> const_iterator +inline auto MemArena::frozen_begin() const -> const_iterator { return _frozen.begin(); } -inline auto -MemArena::frozen_end() const -> const_iterator +inline auto MemArena::frozen_end() const -> const_iterator { return _frozen.end(); } +template <typename T> FixedArena<T>::FixedArena(MemArena &arena) : _arena(arena) { + static_assert(sizeof(T) >= sizeof(T*)); +} + +template <typename T> template <typename... Args> T *FixedArena<T>::make(Args... args) +{ + if (_list._next) { + void* t = _list._next; + _list._next = _list._next->_next; + return new (t) T(std::forward(args)...); + } + return _arena.template make<T>(std::forward(args)...); +} + +template <typename T> void FixedArena<T>::destroy(T *t) +{ + if (t) { + t->~T(); // destructor. + auto item = reinterpret_cast<Item *>(t); + item->_next = _list._next; + _list._next = item; + } +} + } // namespace swoc diff --git a/unit_tests/test_MemArena.cc b/unit_tests/test_MemArena.cc index 9a76fc7..a3d6ace 100644 --- a/unit_tests/test_MemArena.cc +++ b/unit_tests/test_MemArena.cc @@ -26,6 +26,7 @@ using swoc::MemSpan; using swoc::MemArena; +using swoc::FixedArena; using std::string_view; using swoc::TextView; using namespace std::literals; @@ -358,3 +359,33 @@ TEST_CASE("MemArena temporary", "[libswoc][MemArena][tmp]") } REQUIRE(arena.reserved_size() == rsize); } + +TEST_CASE("FixedArena", "[libswoc][FixedArena]") { + struct Thing { + int x = 0; + std::string name; + }; + MemArena arena; + FixedArena<Thing> fa{arena}; + + Thing * one = fa.make(); + Thing * two = fa.make(); + two->x = 17; + two->name = "Bob"; + fa.destroy(two); + Thing * three = fa.make(); + REQUIRE(three == two); // reused instance. + REQUIRE(three->x == 0); // but reconstructed. + REQUIRE(three->name.empty() == true); + fa.destroy(three); + std::array<Thing*, 17> things; + for ( auto & ptr : things ) { + ptr = fa.make(); + } + two = things[things.size() - 1]; + for ( auto & ptr : things) { + fa.destroy(ptr); + } + three = fa.make(); + REQUIRE(two == three); +};
