6#include <memory_resource>
115template <
int PtrBits = TF_POINTER_BITS>
117 static_assert(64 - PtrBits >= 16,
118 "TaggedHead64 requires at least 16 tag bits for ABA safety "
119 "(PtrBits must be <= 48); use TaggedHead128 instead");
197 std::atomic<ObjectBlock*> next_free {
nullptr};
199 alignas(T) std::byte storage[
sizeof(T)];
202 T* object() noexcept {
203 return std::launder(
reinterpret_cast<T*
>(storage));
207 const T* object() const noexcept {
208 return std::launder(
reinterpret_cast<const T*
>(storage));
218 static ObjectBlock* from_object(T* obj)
noexcept {
219 return reinterpret_cast<ObjectBlock*
>(
220 reinterpret_cast<char*
>(obj) - offsetof(ObjectBlock, storage)
319template <
typename T,
typename H = TaggedHead128,
size_t LogSize = 5>
322 static_assert(LogSize >= 1 && LogSize <= 15,
323 "LogSize must be in [1, 15]");
325 using Block = ObjectBlock<T>;
327 static constexpr size_t NumPools = 1u << LogSize;
329 struct alignas(TF_CACHELINE_SIZE) Shard {
334 std::atomic<H> _free_head {H{}};
339 alignas(TF_CACHELINE_SIZE) std::pmr::synchronized_pool_resource _backing {
340 std::pmr::pool_options {
341 .max_blocks_per_chunk = 1024,
342 .largest_required_pool_block =
sizeof(Block)
346 void push_free(Block* b)
noexcept {
347 H cur = _free_head.load(std::memory_order_relaxed);
354 reinterpret_cast<Block*
>(cur.get_ptr()), std::memory_order_relaxed);
356 reinterpret_cast<typename H::pointer_type
>(b),
357 static_cast<typename H::tag_type
>(cur.get_tag() + 1)
359 }
while (!_free_head.compare_exchange_weak(
361 std::memory_order_release,
362 std::memory_order_relaxed
366 Block* pop_free()
noexcept {
367 H cur = _free_head.load(std::memory_order_acquire);
368 while (cur.get_ptr()) {
369 auto* p =
reinterpret_cast<Block*
>(cur.get_ptr());
374 Block* nx = p->next_free.load(std::memory_order_relaxed);
376 reinterpret_cast<typename H::pointer_type
>(nx),
377 static_cast<typename H::tag_type
>(cur.get_tag() + 1)
379 if (_free_head.compare_exchange_weak(
381 std::memory_order_acquire,
382 std::memory_order_acquire
390 Block* allocate_from_backing() {
391 return static_cast<Block*
>(
392 _backing.allocate(
sizeof(Block),
alignof(Block))
396 void deallocate_to_backing(Block* b) {
397 _backing.deallocate(b,
sizeof(Block),
alignof(Block));
401 std::array<Shard, NumPools> _shards;
412 static size_t _next_shard()
noexcept {
413 thread_local size_t counter =
414 std::hash<std::thread::id>{}(std::this_thread::get_id());
415 return counter++ & (NumPools - 1);
490 template <
typename... Args>
492 auto sid = _next_shard();
493 auto& shard = _shards[sid];
495 Block* block = shard.pop_free();
496 if (!block) block = shard.allocate_from_backing();
498 block->pool_id =
static_cast<uint16_t
>(sid);
499 return std::construct_at(block->object(), std::forward<Args>(args)...);
535 auto* block = Block::from_object(obj);
536 std::destroy_at(block->object());
537 _shards[block->pool_id].push_free(block);
583 for (
auto& shard : _shards) {
587 shard._backing.release();
591 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:491
~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:533
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:582
taskflow namespace
Definition small_vector.hpp:20
pointer_type get_ptr() const noexcept
returns the stored block address
Definition object_pool.hpp:63
TaggedHead128()=default
constructs a null, zero-tagged head
pointer_type ptr
block address (reinterpret-cast to/from ObjectBlock*)
Definition object_pool.hpp:49
tag_type tag
ABA version counter; incremented on every push and pop.
Definition object_pool.hpp:50
TaggedHead128(pointer_type p, tag_type t) noexcept
constructs a head with an explicit block address and version counter
Definition object_pool.hpp:60
uintptr_t pointer_type
block address representation
Definition object_pool.hpp:46
uintptr_t tag_type
ABA version counter representation.
Definition object_pool.hpp:47
tag_type get_tag() const noexcept
returns the ABA version counter
Definition object_pool.hpp:66
uint16_t tag_type
ABA version counter.
Definition object_pool.hpp:122
uintptr_t pointer_type
block address representation
Definition object_pool.hpp:121
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:146
pointer_type get_ptr() const noexcept
returns the block address
Definition object_pool.hpp:143
static constexpr int PTR_BITS
bits reserved for the block address
Definition object_pool.hpp:124
TaggedHead64(pointer_type p, tag_type t) noexcept
constructs a head with an explicit block address and version counter
Definition object_pool.hpp:139
uintptr_t bits
packed word: high TAG_BITS bits = version tag, low PTR_BITS bits = address
Definition object_pool.hpp:129
static constexpr int TAG_BITS
bits reserved for the version counter
Definition object_pool.hpp:125
static constexpr pointer_type PTR_MASK
mask isolating the address field
Definition object_pool.hpp:126