6#include <memory_resource>
151template <
int PtrBits = TF_POINTER_BITS>
153 static_assert(64 - PtrBits >= 16,
154 "TaggedHead64 requires at least 16 tag bits for ABA safety "
155 "(PtrBits must be <= 48); use TaggedHead128 instead");
278 std::atomic<ObjectBlock*> next_free {
nullptr};
283 alignas(T) std::byte storage[
sizeof(T)];
288 T* object() noexcept {
289 return std::launder(
reinterpret_cast<T*
>(storage));
295 const T* object() const noexcept {
296 return std::launder(
reinterpret_cast<const T*
>(storage));
306 static ObjectBlock* from_object(T* obj)
noexcept {
307 return reinterpret_cast<ObjectBlock*
>(
308 reinterpret_cast<char*
>(obj) - offsetof(ObjectBlock, storage)
407template <
typename T,
typename H = TaggedHead128,
size_t LogSize = 5>
410 static_assert(LogSize >= 1 && LogSize <= 15,
411 "LogSize must be in [1, 15]");
413 using Block = ObjectBlock<T>;
415 static constexpr size_t NumPools = 1u << LogSize;
417 struct alignas(TF_CACHELINE_SIZE) Shard {
422 std::atomic<H> _free_head {H{}};
427 alignas(TF_CACHELINE_SIZE) std::pmr::synchronized_pool_resource _backing {
428 std::pmr::pool_options {
429 .max_blocks_per_chunk = 1024,
430 .largest_required_pool_block =
sizeof(Block)
434 void push_free(Block* b)
noexcept {
435 H cur = _free_head.load(std::memory_order_relaxed);
442 reinterpret_cast<Block*
>(cur.get_ptr()), std::memory_order_relaxed);
444 reinterpret_cast<typename H::pointer_type
>(b),
445 static_cast<typename H::tag_type
>(cur.get_tag() + 1)
447 }
while (!_free_head.compare_exchange_weak(
449 std::memory_order_release,
450 std::memory_order_relaxed
454 Block* pop_free()
noexcept {
455 H cur = _free_head.load(std::memory_order_acquire);
456 while (cur.get_ptr()) {
457 auto* p =
reinterpret_cast<Block*
>(cur.get_ptr());
462 Block* nx = p->next_free.load(std::memory_order_relaxed);
464 reinterpret_cast<typename H::pointer_type
>(nx),
465 static_cast<typename H::tag_type
>(cur.get_tag() + 1)
467 if (_free_head.compare_exchange_weak(
469 std::memory_order_acquire,
470 std::memory_order_acquire
478 Block* allocate_from_backing() {
479 return static_cast<Block*
>(
480 _backing.allocate(
sizeof(Block),
alignof(Block))
484 void deallocate_to_backing(Block* b) {
485 _backing.deallocate(b,
sizeof(Block),
alignof(Block));
489 std::array<Shard, NumPools> _shards;
500 static size_t _next_shard()
noexcept {
501 thread_local size_t counter =
502 std::hash<std::thread::id>{}(std::this_thread::get_id());
503 return counter++ & (NumPools - 1);
578 template <
typename... Args>
580 auto sid = _next_shard();
581 auto& shard = _shards[sid];
583 Block* block = shard.pop_free();
584 if (!block) block = shard.allocate_from_backing();
586 block->pool_id =
static_cast<uint16_t
>(sid);
587 return std::construct_at(block->object(), std::forward<Args>(args)...);
623 auto* block = Block::from_object(obj);
624 std::destroy_at(block->object());
625 _shards[block->pool_id].push_free(block);
671 for (
auto& shard : _shards) {
675 shard._backing.release();
679 shard._free_head.store(H{}, std::memory_order_relaxed);
ObjectPool(const ObjectPool &)=delete
disabled copy constructor
ObjectPool()=default
constructs the allocator with 2^LogSize empty shards
T * animate(Args &&... args)
constructs an object of type T in the pool and returns a pointer
Definition object_pool.hpp:579
~ObjectPool()=default
destroys the allocator and releases all backing memory to upstream
void recycle(T *obj)
destructs the object and returns its storage to the pool
Definition object_pool.hpp:621
ObjectPool & operator=(const ObjectPool &)=delete
disabled copy assignment operator
void release()
returns all recycled blocks and backing memory to the system allocator
Definition object_pool.hpp:670
taskflow namespace
Definition small_vector.hpp:20
pointer_type get_ptr() const noexcept
returns the stored block address
Definition object_pool.hpp:92
TaggedHead128()=default
constructs a null, zero-tagged head
pointer_type ptr
block address (reinterpret-cast to/from ObjectBlock*)
Definition object_pool.hpp:60
tag_type tag
ABA version counter; incremented on every push and pop.
Definition object_pool.hpp:65
TaggedHead128(pointer_type p, tag_type t) noexcept
constructs a head with an explicit block address and version counter
Definition object_pool.hpp:82
uintptr_t pointer_type
block address representation
Definition object_pool.hpp:50
uintptr_t tag_type
ABA version counter representation.
Definition object_pool.hpp:55
tag_type get_tag() const noexcept
returns the ABA version counter
Definition object_pool.hpp:102
uint16_t tag_type
ABA version counter representation.
Definition object_pool.hpp:165
uintptr_t pointer_type
block address representation
Definition object_pool.hpp:160
TaggedHead64()=default
constructs a null, zero-tagged head
tag_type get_tag() const noexcept
returns the 16-bit ABA version counter
Definition object_pool.hpp:224
pointer_type get_ptr() const noexcept
returns the block address
Definition object_pool.hpp:214
static constexpr int PTR_BITS
bits reserved for the block address
Definition object_pool.hpp:170
TaggedHead64(pointer_type p, tag_type t) noexcept
constructs a head with an explicit block address and version counter
Definition object_pool.hpp:203
uintptr_t bits
packed word: high TAG_BITS bits hold the version tag, low PTR_BITS bits hold the address
Definition object_pool.hpp:186
static constexpr int TAG_BITS
bits reserved for the version counter
Definition object_pool.hpp:175
static constexpr pointer_type PTR_MASK
mask isolating the address field
Definition object_pool.hpp:180