Struct RegionAllocator
This struct provides an interface to the RegionAllocator
functionality
and enforces scoped deletion. A new instance using the thread-local
RegionAllocatorStack
instance is created using the global
newRegionAllocator
function. A new instance using
an explicitly created RegionAllocatorStack
is created using
RegionAllocatorStack
.
struct RegionAllocator
;
Each instance has reference semantics in that any copy will allocate from the
same memory. When the last copy of an instance goes out of scope, all memory
allocated via that instance is freed. Only the most recently created
still-existing RegionAllocator
using a given RegionAllocatorStack
may be used to allocate and free memory at any given time. Deviations
from this model result in a RegionAllocatorException
being thrown.
An uninitialized RegionAllocator
(for example RegionAllocator
)
has semantics similar to a null pointer. It may be assigned to or passed to
a function. However, any attempt to call a method will result in a
RegionAllocatorException
being thrown.
Properties
Name | Type | Description |
---|---|---|
gcScanned [get]
|
bool | Returns whether the RegionAllocatorStack used by this
RegionAllocator instance is scanned by the garbage collector.
|
segmentSize [get]
|
ulong | Returns the segment size of the RegionAllocatorStack used by this
RegionAllocator .
|
segmentSlack [get]
|
ulong | Returns the maximum number of bytes that may be allocated in the current segment. |
Methods
Name | Description |
---|---|
alignBytes
|
Returns the number of bytes to which an allocation of size nBytes is guaranteed to be aligned. |
allocate
|
Allocates nBytes bytes on the RegionAllocatorStack used by this
RegionAllocator instance. The last block allocated from this
RegionAllocator instance can be freed by calling
RegionAllocator or RegionAllocator or will be
automatically freed when the last copy of this RegionAllocator
instance goes out of scope.
|
allocSize
|
Returns the number of bytes used to satisfy an allocation request
of nBytes . Will return a value greater than or equal to
nBytes to account for alignment overhead.
|
array
|
Copies range to an array. The array will be located on the
RegionAllocator stack if any of the following conditions apply:
|
free
|
Checks that ptr is a pointer to the block that would be freed by
freeLast then calls freeLast . Throws a
RegionAllocatorException if the pointer does not point to the
block that would be freed by freeLast .
|
freeLast
|
Frees the last block of memory allocated by the current
RegionAllocator . Throws a RegionAllocatorException if
this RegionAllocator is not the most recently created still-existing
RegionAllocator using its RegionAllocatorStack instance.
|
newArray
|
Allocates an array of type T . T may be a multidimensional
array. In this case sizes may be specified for any number of dimensions
from 1 to the number in T .
|
opAssign
|
|
resize
|
Attempts to resize a previously allocated block of memory in place.
This is possible only if ptr points to the beginning of the last
block allocated by this RegionAllocator instance and, in the
case where newSize is greater than the old size, there is
additional space in the segment that ptr was allocated from.
Additionally, blocks larger than this RegionAllocator 's segment size
cannot be grown or shrunk.
|
uninitializedArray
|
Same as newArray , except skips initialization of elements for
performance reasons.
|
Examples
void foo() {
auto alloc = newRegionAllocator();
auto ptr1 = bar(alloc);
auto ptr2 = alloc .allocate(42);
// The last copy of the RegionAllocator object used to allocate ptr1
// and ptr2 is going out of scope here. The memory pointed to by
// both ptr1 and ptr2 will be freed.
}
void* bar(RegionAllocator alloc) {
auto ret = alloc .allocate(42);
auto alloc2 = newRegionAllocator();
auto ptr3 = alloc2 .allocate(42);
// ptr3 was allocated using alloc2, which is going out of scope.
// Its memory will therefore be freed. ret was allocated using alloc.
// A copy of this RegionAllocator is still alive in foo() after
// bar() executes. Therefore, ret will not be freed on returning and
// is still valid after bar() returns.
return ret;
}
void* thisIsSafe() {
// This is safe because the two RegionAllocator objects being used
// are using two different RegionAllocatorStack objects.
auto alloc = newRegionAllocator();
auto ptr1 = alloc .allocate(42);
auto stack = RegionAllocatorStack(1_048_576, GCScan .no);
auto alloc2 = stack .newRegionAllocator();
auto ptr2 = alloc2 .allocate(42);
auto ptr3 = alloc .allocate(42);
}
void* dontDoThis() {
auto alloc = newRegionAllocator();
auto ptr1 = alloc .allocate(42);
auto alloc2 = newRegionAllocator();
// Error: Allocating from a RegionAllocator instance other than the
// most recently created one that's still alive from a given stack.
auto ptr = alloc .allocate(42);
}
void uninitialized() {
RegionAllocator alloc;
auto ptr = alloc .allocate(42); // Error: alloc is not initialized.
auto alloc2 = alloc; // Ok. Both alloc, alloc2 are uninitialized.
alloc2 = newRegionAllocator();
auto ptr2 = alloc2 .allocate(42); // Ok.
auto ptr3 = alloc .allocate(42); // Error: alloc is still uninitialized.
alloc = alloc2;
auto ptr4 = alloc .allocate(42); // Ok.
}
Note
Allocations larger than this
are handled as a special
case and fall back to allocating directly from the C heap. These large
allocations are freed as if they were allocated on a RegionAllocatorStack
when free
or freeLast
is called or the last copy of a
RegionAllocator
instance goes out of scope. However, due to the extra
bookkeeping required, destroying a region (as happens when the last copy of
a RegionAllocator
instance goes out of scope) will require time linear
instead of constant in the number of allocations for regions where these
large allocations are present.