Growable bump arena over a MemoryResource.
More...
#include <EBGeometry_Pool.hpp>
|
| | Pool (MemoryResource &a_resource, size_t a_initialBytes=0) |
| | Construct an empty pool over a_resource, optionally pre-reserving a block.
|
| |
|
| ~Pool () noexcept |
| | Destructor. Returns the block to the backing resource.
|
| |
|
| Pool (const Pool &)=delete |
| | Copy constructor. Deleted: a pool uniquely owns its block.
|
| |
|
Pool & | operator= (const Pool &)=delete |
| | Copy assignment. Deleted: a pool uniquely owns its block.
|
| |
| | Pool (Pool &&a_other) noexcept |
| | Move constructor. Steals a_other's block and leaves it empty.
|
| |
| Pool & | operator= (Pool &&a_other) noexcept |
| | Move assignment. Frees any current block, then steals a_other's.
|
| |
| uint64_t | reserve (size_t a_count, size_t a_elemSize, size_t a_alignment) |
| | Bump-reserve a_count * a_elemSize bytes aligned to a_alignment.
|
| |
| void | freeze () noexcept |
| | Freeze the block: forbid further reserve, stabilize base, enable mirror.
|
| |
| void * | base () const noexcept |
| | Base address of the block.
|
| |
| const PoolControl * | control () const noexcept |
| | This pool's stable control block, through which pool-resident objects re-resolve the base on every access.
|
| |
| uint64_t | id () const noexcept |
| | Identity of this pool, unique among all pools constructed in this process.
|
| |
| uint64_t | mirrorOf () const noexcept |
| | Identity of the pool this one ultimately mirrors, or zero if it is not a mirror.
|
| |
| uint64_t | rootId () const noexcept |
| | Identity of the root of this pool's mirror chain.
|
| |
| size_t | usedBytes () const noexcept |
| | Bytes currently in use (the bump cursor).
|
| |
| size_t | capacityBytes () const noexcept |
| | Bytes currently allocated for the block.
|
| |
| bool | isFrozen () const noexcept |
| | Whether the pool has been frozen.
|
| |
| MemoryResource & | resource () const noexcept |
| | The backing memory resource.
|
| |
Growable bump arena over a MemoryResource.
Move-only, single-owner RAII. See the file-level documentation for how growth, freezing and mirroring interact, and reserve for the resolve-use-discard rule.
- Note
- This is one of the two deliberately non-trivial types of the memory foundation (it owns a block and a resource pointer with RAII semantics); everything stored inside it is trivially copyable POD, but the pool itself is not, so no
std::is_trivially_copyable_v static_assert applies here.
◆ Pool() [1/2]
| EBGeometry::Pool::Pool |
( |
MemoryResource & |
a_resource, |
|
|
size_t |
a_initialBytes = 0 |
|
) |
| |
|
explicit |
Construct an empty pool over a_resource, optionally pre-reserving a block.
Aborts, in every build, if a_resource is not host-accessible or if a_initialBytes is more than a block can hold (the largest multiple of PoolBaseAlign a size_t holds).
- Parameters
-
| [in] | a_resource | Memory resource that backs every allocation of this pool. Must outlive the pool. |
| [in] | a_initialBytes | If non-zero, the initial block byte capacity to reserve up front (avoids the first grow). The block is still empty (usedBytes()==0). |
◆ Pool() [2/2]
| EBGeometry::Pool::Pool |
( |
Pool && |
a_other | ) |
|
|
noexcept |
Move constructor. Steals a_other's block and leaves it empty.
- Parameters
-
| [in,out] | a_other | Source pool, left owning nothing. |
◆ base()
| void * EBGeometry::Pool::base |
( |
| ) |
const |
|
inlinenoexcept |
Base address of the block.
- Returns
- Pointer to the first byte of the block (null if nothing has been reserved yet, or if this pool has been moved from).
◆ capacityBytes()
| size_t EBGeometry::Pool::capacityBytes |
( |
| ) |
const |
|
inlinenoexcept |
Bytes currently allocated for the block.
- Returns
- Block byte capacity.
◆ control()
| const PoolControl * EBGeometry::Pool::control |
( |
| ) |
const |
|
inlinenoexcept |
This pool's stable control block, through which pool-resident objects re-resolve the base on every access.
The returned address is fixed for the lifetime of the pool and survives every move of the pool object, which is precisely why pool-resident objects store this rather than a Pool*. It does not survive the pool's destruction: the pool must outlive every object reserved from it.
- Returns
- Pointer to the control block (null only for a moved-from pool).
◆ freeze()
| void EBGeometry::Pool::freeze |
( |
| ) |
|
|
noexcept |
Freeze the block: forbid further reserve, stabilize base, enable mirror.
Idempotent and irreversible. The only precondition it establishes is the one mirror needs (a block that can no longer be reallocated); a reserve after it aborts, in every build. Freezing is not required to query anything built in the pool.
◆ id()
| uint64_t EBGeometry::Pool::id |
( |
| ) |
const |
|
inlinenoexcept |
Identity of this pool, unique among all pools constructed in this process.
Assigned at construction from a monotonic counter and never reused; a mirror gets its own fresh identity. Used by mirrorOf to record lineage.
- Returns
- This pool's identity (zero only for a moved-from pool).
◆ isFrozen()
| bool EBGeometry::Pool::isFrozen |
( |
| ) |
const |
|
inlinenoexcept |
Whether the pool has been frozen.
- Returns
- True after freeze.
◆ mirror()
Mirror a frozen pool's whole block into another resource.
Allocates a fresh block of exactly a_src.usedBytes() from a_dstResource, copies the entire block byte-for-byte (via GPU::memcpy for a host/device transfer, or std::memcpy for a host-to-host copy), and returns the result frozen. Every PODVector offset resolves unchanged against the new base – this is the host-to-device mirror (and, host-to-host, an exact independent copy). A device-to-host mirror copies results back; a device-to-device mirror (neither resource host-accessible) aborts, in every build.
- Parameters
-
| [in] | a_src | Source pool. Must be frozen (a_src.isFrozen()); an unfrozen source aborts, in every build. |
| [in] | a_dstResource | Destination resource for the mirrored block. Must outlive the returned pool. |
- Returns
- A new, frozen pool holding a byte-identical copy of
a_src's block.
◆ mirrorOf()
| uint64_t EBGeometry::Pool::mirrorOf |
( |
| ) |
const |
|
inlinenoexcept |
Identity of the pool this one ultimately mirrors, or zero if it is not a mirror.
This names the root of the mirror chain, not the immediate source: mirroring host -> pinned staging -> device leaves the device pool naming the original host pool, not the staging pool. That is what lets a pool-resident object built in the host pool validate a rebase onto the device pool (see EBGeometry::DCEL::MeshT::rebasedView) without having to know how many hops the block traveled.
- Returns
- Identity of the root source pool, or 0 if this pool is not a mirror.
◆ operator=()
| Pool & EBGeometry::Pool::operator= |
( |
Pool && |
a_other | ) |
|
|
noexcept |
Move assignment. Frees any current block, then steals a_other's.
- Parameters
-
| [in,out] | a_other | Source pool, left owning nothing. |
- Returns
- Reference to this pool.
◆ reserve()
| uint64_t EBGeometry::Pool::reserve |
( |
size_t |
a_count, |
|
|
size_t |
a_elemSize, |
|
|
size_t |
a_alignment |
|
) |
| |
Bump-reserve a_count * a_elemSize bytes aligned to a_alignment.
Returns the byte offset of the reserved sub-region from base. May grow the block (on a host-accessible resource only). Forbidden after freeze(): a reserve on a frozen pool, an invalid a_alignment, or a request that, added to the bytes already reserved, is more than a block can hold (the largest multiple of PoolBaseAlign a size_t holds), aborts with a message in every build.
- Warning
- A grow does not extend the block in place: it allocates a new block, copies the live bytes into it, and deallocates the old one. Every address that was already resolved against the old base – a raw pointer, a
T& handed out by PODVector::at or by a pool-resident container's accessor, a PODSpan from PODVector::bind – is dangling afterwards. Every PODVector is unaffected, because it stores a byte offset rather than an address, and so is anything that re-resolves through PoolControl::m_base.
The rule is therefore: resolve, use, discard. A resolved address must not outlive the next reserve on the same pool. To carry data across a reserve, carry it by value and write it back through a fresh resolution:
Edge e = mesh.getEdge(3);
mesh.reserveFaces(pool, n);
e.setFace(7);
mesh.getEdge(3) = e;
Note that the hazard is intermittent – a grow only happens when the request exceeds the current capacity – and its quiet form is worse than a crash: if the freed block is still mapped, a write through a stale address lands in the abandoned copy and is silently lost.
- Parameters
-
| [in] | a_count | Number of elements. |
| [in] | a_elemSize | Size of one element in bytes. |
| [in] | a_alignment | Required alignment of the sub-region (power of two, <= PoolBaseAlign). |
- Returns
- Byte offset of the sub-region from base;
base() + offset is a_alignment-aligned.
◆ resource()
The backing memory resource.
- Returns
- Reference to the resource passed at construction.
◆ rootId()
| uint64_t EBGeometry::Pool::rootId |
( |
| ) |
const |
|
inlinenoexcept |
Identity of the root of this pool's mirror chain.
This pool's own identity if it is not a mirror, else mirrorOf. Two pools with the same root hold byte-identical copies of the root's block, as far as the root's frozen contents go, so a descriptor built in one can be rebased onto any other.
- Returns
- The root identity (zero only for a moved-from pool).
◆ usedBytes()
| size_t EBGeometry::Pool::usedBytes |
( |
| ) |
const |
|
inlinenoexcept |
Bytes currently in use (the bump cursor).
- Returns
- Number of reserved bytes.
The documentation for this class was generated from the following file: