ObjectPool
object_pool.hpp provides a family of free-index-queue-based RAII slot pools in current mainline. The core template is:
LibXR::BasicObjectPool<Data, FreeQueue>
and the three currently exported aliases are:
LibXR::ObjectPool<Data, IndexType>: backed byQueue<IndexType>LibXR::SPSCObjectPool<Data, IndexType>: backed bySPSCQueue<IndexType>LibXR::MPMCObjectPool<Data, IndexType>: backed byMPMCQueue<IndexType>
Unlike LockFreePool, this family focuses on:
- acquiring one exclusive slot through
Acquire() - returning that slot automatically through a move-only
Handle - modifying user objects in place inside the slot
1. Design points
1.1 Minimal queue constraint: PoolIndexQueue
BasicObjectPool does not depend on one specific queue implementation. Instead, it requires the free-index queue to provide a minimal typed interface:
ValueTypePush(const ValueType&)Pop(ValueType&)Size()
That is why current mainline can directly reuse ordinary Queue, SPSCQueue, and MPMCQueue as free-index managers.
1.2 Move-only Handle
On successful acquisition, the pool returns a move-only Handle instead of a raw pointer:
- the handle returns the slot automatically on destruction
- copying is disabled, so one slot cannot be owned by multiple handles accidentally
Get(),operator->(), andoperator*()provide access to the object in the slotIndex()returns the owned slot indexReset()returns the slot early if needed
2. Construction forms in current mainline
BasicObjectPool currently supports four construction patterns:
- internal queue + internal slots
- internal queue + external slots
- external queue + internal slots
- external queue + external slots
Practical meaning:
- if you want the simplest usage, choose internal queue / internal slots
- if slot storage must live in caller-controlled memory, provide external
slots - if you want to reuse or precisely control the queue behavior, provide an external
free_queue
When an external free_queue is used, the current implementation requires:
- the queue must be empty when passed in
- it must be dedicated to the pool
- its capacity must be at least
slot_count
3. Main APIs
3.1 Acquire and return
ErrorCode Acquire(Handle& handle)void Handle::Reset()
Behavior notes:
- success returns
ErrorCode::OK - when no free slot is available, the result comes from the underlying queue pop failure, commonly
ErrorCode::EMPTY - the slot is also returned automatically when the handle is destroyed
3.2 Capacity queries
size_t EmptySize() const: number of currently acquirable free slotssize_t Size() const: total slot count
3.3 Non-owning access
Data& UnsafeAt(size_t index)const Data& UnsafeAt(size_t index) const
These APIs bypass the ownership semantics of Acquire() / Handle. They are intended for debugging, external-storage inspection, or situations where the caller already knows the slot state.
4. The three common aliases
4.1 ObjectPool
template <typename Data, typename IndexType = uint32_t>
using ObjectPool = BasicObjectPool<Data, Queue<IndexType>>;
Suitable for general-purpose object-pool usage in ordinary thread context when strict lock-free queue semantics are not the main concern.
4.2 SPSCObjectPool
template <typename Data, typename IndexType = uint32_t>
using SPSCObjectPool = BasicObjectPool<Data, SPSCQueue<IndexType>>;
Suitable when free-slot acquisition and return clearly follow a single-producer / single-consumer path.
4.3 MPMCObjectPool
template <typename Data, typename IndexType = uint32_t>
using MPMCObjectPool = BasicObjectPool<Data, MPMCQueue<IndexType>>;
Suitable for multi-producer / multi-consumer slot acquisition and return.
5. Example
#include <libxr.hpp>
struct Packet
{
uint32_t id = 0;
uint8_t payload[32] = {};
};
LibXR::ObjectPool<Packet> pool(16);
LibXR::ObjectPool<Packet>::Handle handle;
if (pool.Acquire(handle) == LibXR::ErrorCode::OK)
{
handle->id = 42;
(*handle).payload[0] = 0xAA;
}
// the slot is returned automatically when handle leaves scope
To return early:
handle.Reset();
6. Difference from LockFreePool
- The
ObjectPoolfamily is centered on “exclusive slot ownership + RAII return”. It fits object reuse, temporary buffer leasing, and work-slot borrowing. LockFreePoolis centered on “slot state machine + unordered Put/Get”. It fits high-concurrency caching and delivery patterns without strict acquire/return pairing.
If your scenario is “borrow one slot, modify it exclusively, and return it automatically when done”, the ObjectPool family is usually the better match.