13.11 Storage Management
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
[ Each access-to-object type has an associated storage pool. The storage allocated by an allocator
comes from the pool; instances of Unchecked_Deallocation return storage to the pool. Several access types can share the same pool.]
[A storage pool is a variable of a type in the class rooted at Root_Storage_Pool, which is an abstract limited controlled type. By default, the implementation chooses a standard storage pool for each access-to-object type. The user may define new pool types, and may override the choice of pool for an access-to-object type by specifying Storage_Pool for the type.]
Legality Rules
4If Storage_Pool is specified for a given access type, Storage_Size shall not be specified for it.
Static Semantics
5The following language-defined library package exists:
with Ada.Finalization;
with System.Storage_Elements;
package System.Storage_Pools
with Pure, Nonblocking => False is
7/5type Root_Storage_Pool is
abstract new Ada.Finalization.Limited_Controlled with private
with Preelaborable_Initialization ;
8procedure Allocate(
Pool : in out Root_Storage_Pool;
Storage_Address : out Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is abstract;
9procedure Deallocate(
Pool : in out Root_Storage_Pool;
Storage_Address : in Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is abstract;
10function Storage_Size(Pool : Root_Storage_Pool)
return Storage_Elements.Storage_Count is abstract;
11private
... -- not specified by the language
end System.Storage_Pools;
A storage pool type (or pool type) is a descendant of Root_Storage_Pool. The elements of a storage pool are the objects allocated in the pool by allocator
s.
{8652/0009} For every access-to-object subtype S, the following representation attributes are defined:
S'Storage_Pool
- Denotes the storage pool of the type of S. The type of this attribute is Root_Storage_Pool'Class.
S'Storage_Size- Yields the result of calling Storage_Size(S'Storage_Pool)[, which is intended to be a measure of the number of storage elements reserved for the pool.] The type of this attribute is universal_integer.
Storage_Size or Storage_Pool may be specified for a nonderived access-to-object type via an attribute_definition_clause
; the name
in a Storage_Pool clause shall denote a variable. If the nominal subtype of the name
specified for Storage_Pool is nonblocking (see 9.5), then the primitive Allocate, Deallocate, and Storage_Size subprograms of that type shall be nonblocking. Additionally, if the pool is one that supports subpools (see 13.11.4), the primitive Default_Subpool_for_Pool, Allocate_From_Subpool, and Deallocate_Subpool subprograms shall be nonblocking.
An allocator
of a type T that does not support subpools allocates storage from T's storage pool. If the storage pool is a user-defined object, then the storage is allocated by calling Allocate as described below. Allocator
s for types that support subpools are described in 13.11.4.
allocator
evaluation might result in more than one call upon Allocate. In any case, allocator
s for the access type obtain all the required storage for an object of the designated type by calling the specified Allocate procedure.allocator
s are executed as part of the allocator
of T (as part of the initialization of the object). For instance, an access-to-task type TT may allocate the data structures used to implement the task value from other storage pools. (In particular, the task stack does not necessarily need to be allocated from the storage pool for TT.) If Storage_Pool is not specified for a type defined by an access_to_object_definition
, then the implementation chooses a standard storage pool for it in an implementation-defined manner. In this case, the exception Storage_Error is raised by an allocator
if there is not enough storage. It is implementation defined whether or not the implementation provides user-accessible names for the standard pool type(s).
derived_type_definition
inherits its pool from its parent type, so all access-to-object types in the same derivation class share the same pool. Hence the “defined by an access_to_object_definition
” wording above.The type(s) of the standard pool(s), and the primitive Allocate, Deallocate, and Storage_Size subprograms for the standard pool(s) are nonblocking. Concurrent invocations of these subprograms do not conflict with one another (see 9.10) when applied to standard storage pools.
If Storage_Size is specified for an access type T, an implementation-defined pool P is used for the type. The Storage_Size of P is at least that requested, and the storage for P is reclaimed when the master containing the declaration of the access type is left. If the implementation cannot satisfy the request, Storage_Error is raised at the freezing point of type T. The storage pool P is used only for allocators returning type T or other access types specified to use T'Storage_Pool. Storage_Error is raised by an allocator
returning such a type if the storage space of P is exhausted (additional memory is not allocated). The type of P, and the primitive Allocate, Deallocate, and Storage_Size subprograms of P are nonblocking.
If neither Storage_Pool nor Storage_Size are specified, then the meaning of Storage_Size is implementation defined.
If Storage_Pool is specified for an access type, then the specified pool is used.
The effect of calling Allocate and Deallocate for a standard storage pool directly (rather than implicitly via an allocator
or an instance of Unchecked_Deallocation) is unspecified.
allocator
might put the pool element on a finalization list. If the user directly Deallocates it, instead of calling an instance of Unchecked_Deallocation, then the implementation would probably try to finalize the object upon master completion, which would be bad news. Therefore, the implementation should define such situations as erroneous. Erroneous Execution
22If Storage_Pool is specified for an access type, then if Allocate can satisfy the request, it should allocate a contiguous block of memory, and return the address of the first storage element in Storage_Address. The block should contain Size_In_Storage_Elements storage elements, and should be aligned according to Alignment. The allocated storage should not be used for any other purpose while the pool element remains in existence. If the request cannot be satisfied, then Allocate should propagate an exception [(such as Storage_Error)]. If Allocate behaves in any other manner, then the program execution is erroneous.
Implementation Requirements
22.1/3The Allocate procedure of a user-defined storage pool object P may be called by the implementation only to allocate storage for a type T whose pool is P, only at the following points:
- During the execution of an
allocator
of type T;
aggregate
; this is important if the initializing expression is built in place. We need to allow allocation to be deferred until the size of the object is known. - During the execution of a return statement for a function whose result is built-in-place in the result of an
allocator
of type T;
- During the execution of an assignment operation with a target of an allocated object of type T with a part that has an unconstrained discriminated subtype with defaults.
For each of the calls of Allocate described above, P (equivalent to T'Storage_Pool) is passed as the Pool parameter. The Size_In_Storage_Elements parameter indicates the number of storage elements to be allocated, and is no more than D'Max_Size_In_Storage_Elements, where D is the designated subtype of T. The Alignment parameter is a nonzero integral multiple of D'Alignment if D is a specific type, and otherwise is a nonzero integral multiple of the alignment of the specific type identified by the tag of the object being created; it is unspecified if there is no such value. The Alignment parameter is no more than D'Max_Alignment_For_Allocation. The result returned in the Storage_Address parameter is used as the address of the allocated storage, which is a contiguous block of memory of Size_In_Storage_Elements storage elements. [Any exception propagated by Allocate is propagated by the construct that contained the call.]
The number of calls to Allocate that will be used to implement an allocator
for any particular type is unspecified. The number of calls to Deallocate that will be used to implement an instance of Unchecked_Deallocation (see 13.11.2) for any particular object is the same as the number of Allocate calls for that object.
allocator
allocated two pieces x and y, then an instance of Unchecked_Deallocation might deallocate x and then y, or it might deallocate y and then x. The Deallocate procedure of a user-defined storage pool object P may be called by the implementation to deallocate storage for a type T whose pool is P only at the places when an Allocate call is allowed for P, during the execution of an instance of Unchecked_Deallocation for T, or as part of the finalization of the collection of T. For such a call of Deallocate, P (equivalent to T'Storage_Pool) is passed as the Pool parameter. The value of the Storage_Address parameter for a call to Deallocate is the value returned in the Storage_Address parameter of the corresponding successful call to Allocate. The values of the Size_In_Storage_Elements and Alignment parameters are the same values passed to the corresponding Allocate call. Any exception propagated by Deallocate is propagated by the construct that contained the call.
Documentation Requirements
23/5An implementation shall document the set of values that a user-defined Allocate procedure has to accept for the Alignment parameter. An implementation shall document how the standard storage pool is chosen, and how storage is allocated by standard storage pools.
Implementation Advice
24An implementation should document any cases in which it dynamically allocates heap storage for a purpose other than the evaluation of an allocator
.
allocator
should be documented.A default (implementation-provided) storage pool for an access-to-constant type should not have overhead to support deallocation of individual objects.
allocator
of the type is known at link-time, then the allocation should be performed statically. If, in addition, the initial value of the designated object is known at compile time, the object can be allocated to read-only memory. allocator
s cannot raise Storage_Error due to running out of pool space until the appropriate number of storage elements has been used up. This approximate (possibly rounded-up) value should be used as a maximum; the implementation should not increase the size of the pool on the fly. If the Storage_Size for an access type is specified as zero, then the pool should not take up any storage space, and any allocator
for the type should raise Storage_Error. The storage pool used for an allocator
of an anonymous access type should be determined as follows:
- If the
allocator
is defining a coextension (see 3.10.2) of an object being created by an outerallocator
, then the storage pool used for the outerallocator
should also be used for the coextension; 26.2/2 - For other access discriminants and access parameters, the storage pool should be created at the point of the
allocator
, and be reclaimed when the allocated object becomes inaccessible; 26.3/3 - If the
allocator
defines the result of a function with an access result, the storage pool is determined as though theallocator
were in place of the call of the function. If the call is the operand of a type conversion, the storage pool is that of the target access type of the conversion. If the call is itself defining the result of a function with an access result, this rule is applied recursively; 26.4/5 - Otherwise, a default storage pool should be created at the point where the anonymous access type is elaborated; such a storage pool may have no mechanism for the deallocation of individual objects.
allocator
, and be reclaimed when the designated object becomes inaccessible. For other anonymous access types, the pool should be created at the point where the type is elaborated and may have no mechanism for the deallocation of individual objects.aggregate
s is typically managed.allocator
could be more nested. In this case, a "real" storage pool is required. Note, however, that this storage pool need not support (separate) deallocation, as it is not possible to instantiate Unchecked_Deallocation with an anonymous access type. (If deallocation is needed, the object should be allocated for a named access type and converted.) Thus, deallocation only need happen when the anonymous access type itself goes out of scope; this is similar to the case of an access-to-constant type. allocator
s for the associated access type, are responsible for dealing with any interactions with tasking. In particular: - If the
allocator
s are used in different tasks, they require mutual exclusion. 30 - If they are used inside protected objects, they cannot block.
- If they are used by interrupt handlers (see C.3, “Interrupt Support”), the mutual exclusion mechanism has to work properly in that context.
Examples
33To associate an access type with a storage pool object, the user first declares a pool object of some type derived from Root_Storage_Pool. Then, the user defines its Storage_Pool attribute, as follows:
Pool_Object : Some_Storage_Pool_Type;
35type T is access Designated;
for T'Storage_Pool use Pool_Object;
36/5Another access type can be added to an existing storage pool, via:
for T2'Storage_Pool use T'Storage_Pool;
The semantics of this is implementation defined for a standard storage pool.
As usual, a derivative of Root_Storage_Pool can define additional operations. For example, consider the Mark_Release_Pool_Type defined in 13.11.6, that has two additional operations, Mark and Release, the following is a possible use:
{8652/0041} type Mark_Release_Pool_Type
(Pool_Size : Storage_Elements.Storage_Count)
is new Subpools.Root_Storage_Pool_With_Subpools with private;
-- As defined in package MR_Pool, see 13.11.6
41...
42/5Our_Pool : Mark_Release_Pool_Type (Pool_Size => 2000);
My_Mark : Subpool_Handle ; -- As declared in 13.11.6
43/3type Acc is access ...;
for Acc'Storage_Pool use Our_Pool;
...
44/3My_Mark := Mark(Our_Pool);
... -- Allocate objects using “new (My_Mark) Designated(...)”.
Release(My_Mark); -- Finalize objects and reclaim storage.
Extensions to Ada 83
Wording Changes from Ada 83
Incompatibilities With Ada 95
Extensions to Ada 95
pragma
Preelaborable_Initialization to type Root_Storage_Pool, so that extensions of it can be used to declare default-initialized objects in preelaborated units. Wording Changes from Ada 95
Wording Changes from Ada 2005
allocator
for an anonymous access result type.allocator
with a class-wide designated type comes from the specific type that is allocated.allocator
s, up to Max_Alignment_For_Allocation. This eases implementation in some cases. Extensions to Ada 2012
Wording Changes from Ada 2012
13.11.1 Storage Allocation Attributes
1/3[The Max_Size_In_Storage_Elements and Max_Alignment_For_Allocation attributes may be useful in writing user-defined pool types.]
Static Semantics
2/3For every subtype S, the following attributes are defined:
S'Max_Size_In_Storage_Elements
- Denotes the maximum value for Size_In_Storage_Elements that can be requested by the implementation via Allocate for an access type whose designated subtype is S. The value of this attribute is of type universal_integer.
S'Max_Alignment_For_Allocation
- Denotes the maximum value for Alignment that can be requested by the implementation via Allocate for an access type whose designated subtype is S. The value of this attribute is of type universal_integer.
For a type with access discriminants, if the implementation allocates space for a coextension in the same pool as that of the object having the access discriminant, then these attributes account for any calls on Allocate that can be performed to provide space for such coextensions.
Wording Changes from Ada 95
Extensions to Ada 2005
13.11.2 Unchecked Storage Deallocation
1[ Unchecked storage deallocation of an object designated by a value of an access type is achieved by a call to an instance of the generic procedure Unchecked_Deallocation.]
Static Semantics
2The following language-defined generic library procedure exists:
generic
type Object(<>) is limited private;
type Name is access Object;
procedure Ada.Unchecked_Deallocation(X : in out Name)
with Preelaborate, Nonblocking,
Global => in out Name'Storage_Pool,
Convention => Intrinsic;
Legality Rules
3.1/3A call on an instance of Unchecked_Deallocation is illegal if the actual access type of the instance is a type for which the Storage_Size has been specified by a static expression with value zero or is defined by the language to be zero. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit.
allocator
s. We could have left the last sentence out, as a call to Unchecked_Deallocation cannot occur in a specification as it is a procedure call, but we left it for consistency and to avoid future maintenance hazards. Dynamic Semantics
4Given an instance of Unchecked_Deallocation declared as follows:
procedure Free is
new Ada.Unchecked_Deallocation(
object_subtype_name, access_to_variable_subtype_name);
Procedure Free has the following effect:
a)
- After executing Free(X), the value of X is null.
b)- Free(X), when X is already equal to null, has no effect.
c)- Free(X), when X is not equal to null first performs finalization of the object designated by X (and any coextensions of the object — see 3.10.2), as described in 7.6.1. It then deallocates the storage occupied by the object designated by X (and any coextensions). If the storage pool is a user-defined object, then the storage is deallocated by calling Deallocate as described in 13.11. There is one exception: if the object being freed contains tasks, it is unspecified whether the object is deallocated.
After the finalization step of Free(X), the object designated by X, and any subcomponents (and coextensions) thereof, no longer exist; their storage can be reused for other purposes.
Bounded (Run-Time) Errors
11It is a bounded error to free a discriminated, unterminated task object. The possible consequences are:
- No exception is raised.
- Program_Error or Tasking_Error is raised at the point of the deallocation.
- Program_Error or Tasking_Error is raised in the task the next time it references any of the discriminants.
In the first two cases, the storage for the discriminants (and for any enclosing object if it is designated by an access discriminant of the task) is not reclaimed prior to task termination.
An access value that designates a nonexistent object is called a dangling reference.
[If a dangling reference is dereferenced (implicitly or explicitly), execution is erroneous (see below).] If there is no explicit or implicit dereference, then it is a bounded error to evaluate an expression whose result is a dangling reference. If the error is detected, either Constraint_Error or Program_Error is raised. Otherwise, execution proceeds normally, but with the possibility that the access value designates some other existing object.
Erroneous Execution
16/3Evaluating a name that denotes a nonexistent object, or a protected subprogram or subprogram renaming whose associated object (if any) is nonexistent, is erroneous. The execution of a call to an instance of Unchecked_Deallocation is erroneous if the object was created other than by an allocator
for an access type whose pool is Name'Storage_Pool.
Implementation Advice
17For a standard storage pool, Free should actually reclaim the storage.
A call on an instance of Unchecked_Deallocation with a nonnull access value should raise Program_Error if the actual access type of the instance is a type for which the Storage_Size has been specified to be zero or is defined by the language to be zero.
Wording Changes from Ada 95
Wording Changes from Ada 2005
Inconsistencies With Ada 2012
Wording Changes from Ada 2012
13.11.3 Default Storage Pools
1/4[Pragma and aspect Default_Storage_Pool specify the storage pool that will be used in the absence of an explicit specification of a storage pool or storage size for an access type.]
Syntax
2/3The form of a pragma
Default_Storage_Pool is as follows:
pragma Default_Storage_Pool (storage_pool_indicator
);
storage_pool_indicator
::=
storage_pool_name
| null | Standard
3.2/3A pragma
Default_Storage_Pool is allowed immediately within the visible part of a package_specification
, immediately within a declarative_part
, or as a configuration pragma.
Name Resolution Rules
3.3/3The storage_pool_name
is expected to be of type Root_Storage_Pool'Class.
Legality Rules
4/3The storage_pool_name
shall denote a variable.
The Standard storage_pool_indicator
is an identifier specific to a pragma (see 2.8) and does not denote any declaration. If the storage_pool_indicator
is Standard, then there shall not be a declaration with defining_identifier
Standard that is immediately visible at the point of the pragma, other than package Standard itself.
storage_pool_indicator
resolve to package Standard rather than being an identifier specific to a pragma. That would eliminate the need for a special check. But it would be bizarre to have something that could resolve to either an object or a (single) package, and resolving to package Standard would imply that the standard pool is an object declared in that package. A storage pool object must be a variable (see 13.11), yet preelaborable packages depend on package Standard, which would require implementers to implement the standard storage pool with Preelaborable_Initialization, which is an unnecessary restriction.defining_identifier
Standard. If the pragma
is used as a configuration pragma, the storage_pool_indicator
shall be either null or Standard, and it defines the default pool to be the given storage_pool_indicator
within all applicable compilation units (see 10.1.5), except within the immediate scope of another pragma
Default_Storage_Pool. Otherwise, [the pragma occurs immediately within a sequence of declarations, and] it defines the default pool within the immediate scope of the pragma to be the given storage_pool_indicator
, except within the immediate scope of a later pragma Default_Storage_Pool. [Thus, an inner pragma overrides an outer one.]
A pragma
Default_Storage_Pool shall not be used as a configuration pragma that applies to a compilation unit that is within the immediate scope of another pragma
Default_Storage_Pool.
package Parent is
pragma Default_Storage_Pool(...);
...
end Parent;
4.c/3pragma Default_Storage_Pool(...); -- Illegal!
package Parent.Child is
...
end Parent.Child;
Static Semantics
5/4The language-defined aspect Default_Storage_Pool may be specified for a generic instance; it defines the default pool for access types within an instance. .
The Default_Storage_Pool aspect may be specified as Standard, which is an identifier specific to an aspect (see 13.1.1) and defines the default pool to be Standard. In this case, there shall not be a declaration with defining_identifier
Standard that is immediately visible at the point of the aspect specification, other than package Standard itself.
Otherwise, the expected type for the Default_Storage_Pool aspect is Root_Storage_Pool'Class and the aspect_definition
shall be a name
that denotes a variable. This aspect overrides any Default_Storage_Pool pragma that applies to the generic unit; if the aspect is not specified, the default pool of the instance is that defined for the generic unit.
The effect of specifying the aspect Default_Storage_Pool on an instance of a language-defined generic unit is implementation-defined.
For nonderived access types declared in places where the default pool is defined by the pragma or aspect, their Storage_Pool or Storage_Size attribute is determined as follows, unless Storage_Pool or Storage_Size is specified for the type:
- If the default pool is null, the Storage_Size attribute is defined by the language to be zero. [Therefore, an
allocator
for such a type is illegal.] 6.2/4 - If the default pool is neither null nor Standard, the Storage_Pool attribute is that pool.
Otherwise (including when the default pool is specified as Standard), the standard storage pool is used for the type as described in 13.11.
This paragraph was deleted.
Implementation Permissions
8/3An object created by an allocator
that is passed as the actual parameter to an access parameter may be allocated on the stack, and automatically reclaimed, regardless of the default pool.
Wording Changes from Ada 83
Incompatibilities With Ada 2005
Extensions to Ada 2005
Wording Changes from Ada 2005
Extensions to Ada 2012
Wording Changes from Ada 2012
13.11.4 Storage Subpools
1/3This subclause defines a package to support the partitioning of a storage pool into subpools. A subpool may be specified as the default to be used for allocation from the associated storage pool, or a particular subpool may be specified as part of an allocator
(see 4.8).
Static Semantics
2/3The following language-defined library package exists:
package System.Storage_Pools.Subpools
with Preelaborate, Global => in out synchronized is
4/5type Root_Storage_Pool_With_Subpools is
abstract new Root_Storage_Pool with private
with Preelaborable_Initialization;
5/5type Root_Subpool is abstract tagged limited private
with Preelaborable_Initialization;
6/3type Subpool_Handle is access all Root_Subpool'Class;
for Subpool_Handle'Storage_Size use 0;
7/3function Create_Subpool (Pool : in out Root_Storage_Pool_With_Subpools)
return not null Subpool_Handle is abstract;
8/3-- The following operations are intended for pool implementers:
9/3function Pool_of_Subpool (Subpool : not null Subpool_Handle)
return access Root_Storage_Pool_With_Subpools'Class;
10/5procedure Set_Pool_of_Subpool (
Subpool : in not null Subpool_Handle;
To : in out Root_Storage_Pool_With_Subpools'Class)
with Global => overriding in out Subpool;
11/5procedure Allocate_From_Subpool (
Pool : in out Root_Storage_Pool_With_Subpools;
Storage_Address : out Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count;
Subpool : in not null Subpool_Handle) is abstract
with Pre'Class => Pool_of_Subpool(Subpool) = Pool'Access,
Global => overriding in out Subpool;
12/3procedure Deallocate_Subpool (
Pool : in out Root_Storage_Pool_With_Subpools;
Subpool : in out Subpool_Handle) is abstract
with Pre'Class => Pool_of_Subpool(Subpool) = Pool'Access;
13/3function Default_Subpool_for_Pool (
Pool : in out Root_Storage_Pool_With_Subpools)
return not null Subpool_Handle;
14/3overriding
procedure Allocate (
Pool : in out Root_Storage_Pool_With_Subpools;
Storage_Address : out Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count);
15/3overriding
procedure Deallocate (
Pool : in out Root_Storage_Pool_With_Subpools;
Storage_Address : in Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is null;
16/3overriding
function Storage_Size (Pool : Root_Storage_Pool_With_Subpools)
return Storage_Elements.Storage_Count
is (Storage_Elements.Storage_Count'Last);
17/3private
... -- not specified by the language
end System.Storage_Pools.Subpools;
18/3A subpool is a separately reclaimable portion of a storage pool, identified by an object of type Subpool_Handle (a subpool handle). A subpool handle also identifies the enclosing storage pool, a storage pool that supports subpools, which is a storage pool whose type is descended from Root_Storage_Pool_With_Subpools. A subpool is created by calling Create_Subpool or a similar constructor; the constructor returns the subpool handle.
A subpool object is an object of a type descended from Root_Subpool. [Typically, subpool objects are managed by the containing storage pool; only the handles have to be exposed to clients of the storage pool. Subpool objects are designated by subpool handles, and are the run-time representation of a subpool.]
Each subpool belongs to a single storage pool [(which will always be a pool that supports subpools)]. An access to the pool that a subpool belongs to can be obtained by calling Pool_of_Subpool with the subpool handle. Set_Pool_of_Subpool causes the subpool of the subpool handle to belong to the given pool[; this is intended to be called from subpool constructors like Create_Subpool.] Set_Pool_of_Subpool propagates Program_Error if the subpool already belongs to a pool. If Set_Pool_of_Subpool has not yet been called for a subpool, Pool_of_Subpool returns null.
When an allocator
for a type whose storage pool supports subpools is evaluated, a call is made on Allocate_From_Subpool passing in a Subpool_Handle, in addition to the parameters as defined for calls on Allocate (see 13.11). The subpool designated by the subpool_handle_name
is used, if specified in an allocator
. Otherwise, Default_Subpool_for_Pool of the Pool is used to provide a subpool handle. All requirements on the Allocate procedure also apply to Allocate_from_Subpool.
Legality Rules
22/3If a storage pool that supports subpools is specified as the Storage_Pool for an access type, the access type is called a subpool access type. A subpool access type shall be a pool-specific access type.
The accessibility level of a subpool access type shall not be statically deeper than that of the storage pool object. If the specified storage pool object is a storage pool that supports subpools, then the name
that denotes the object shall not denote part of a formal parameter, nor shall it denote part of a dereference of a value of a non-library-level general access type. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit.
Dynamic Semantics
24/3When an access type with a specified storage pool is frozen (see 13.14), if the tag of the storage pool object identifies a storage pool that supports subpools, the following checks are made:
- the
name
used to specify the storage pool object does not denote part of a formal parameter nor part of a dereference of a value of a non-library-level general access type; and 26/3 - the accessibility level of the access type is not deeper than that of the storage pool object.
Program_Error is raised if either of these checks fail.
A call to Subpools.Allocate(P, Addr, Size, Align) does the following:
Allocate_From_Subpool
(Root_Storage_Pool_With_Subpools'Class(P),
Addr, Size, Align,
Subpool => Default_Subpool_for_Pool
(Root_Storage_Pool_With_Subpools'Class(P)));
An allocator
that allocates in a subpool raises Program_Error if the allocated object has task parts.
Unless overridden, Default_Subpool_for_Pool propagates Program_Error.
Erroneous Execution
31.1/4If Allocate_From_Subpool does not meet one or more of the requirements on the Allocate procedure as given in the Erroneous Execution rules of 13.11, then the program execution is erroneous.
Implementation Permissions
32/3When an allocator for a type whose storage pool is of type Root_Storage_Pool'Class is evaluated, but supports subpools, the implementation may call Allocate rather than Allocate_From_Subpool. [This will have the same effect, so long as Allocate has not been overridden.]
allocator
with no subpool_specification
. Note that the "supports subpools" property is not known at compile time for a pool of the class-wide type.- The implementation can dispatch to Storage_Pools.Allocate. If the pool supports subpools, this will call Allocate_From_Subpool with the default subpool so long as Allocate has not been overridden.
- The implementation can declare Allocate_From_Subpool as a primitive of Root_Storage_Pool in the private part of Storage_Pools. This means that the Allocate_From_Subpool for Root_Storage_Pool_With_Subpools overrides that private one. The implementation can thus call the private one, which will call Allocate for non-subpool-supporting pools. The effect of this implementation does not change if Allocate is overridden for a pool that supports subpools.
Extensions to Ada 2005
Wording Changes from Ada 2012
13.11.5 Subpool Reclamation
1/3A subpool may be explicitly deallocated using Unchecked_Deallocate_Subpool.
Static Semantics
2/3The following language-defined library procedure exists:
with System.Storage_Pools.Subpools;
procedure Ada.Unchecked_Deallocate_Subpool
(Subpool : in out System.Storage_Pools.Subpools.Subpool_Handle)
with Global => in out all;
If Subpool is null, a call on Unchecked_Deallocate_Subpool has no effect. Otherwise, the subpool is finalized, and Subpool is set to null.
Finalization of a subpool has the following effects in the given order:
This paragraph was deleted.a)
-
b)- Any of the objects allocated from the subpool that still exist are finalized in an arbitrary order;
c)- All of the objects allocated from the subpool cease to exist;
d)- The following [dispatching] call is then made:
Deallocate_Subpool(Pool_of_Subpool(Subpool).all, Subpool);
e)
- The subpool ceases to belong to any pool.
Finalization of a Root_Storage_Pool_With_Subpools object finalizes all subpools that belong to that pool that have not yet been finalized.
Extensions to Ada 2005
Wording Changes from Ada 2012
13.11.6 Storage Subpool Example
Examples
1/3The following example is a simple but complete implementation of the classic Mark/Release pool using subpools:
with System.Storage_Pools.Subpools;
with System.Storage_Elements;
with Ada.Unchecked_Deallocate_Subpool;
package MR_Pool is
3/3use System.Storage_Pools;
-- For uses of Subpools.
use System.Storage_Elements;
-- For uses of Storage_Count and Storage_Array.
4/3-- Mark and Release work in a stack fashion, and allocations are not allowed
-- from a subpool other than the one at the top of the stack. This is also
-- the default pool.
5/3subtype Subpool_Handle is Subpools.Subpool_Handle;
6/3type Mark_Release_Pool_Type (Pool_Size : Storage_Count) is new
Subpools.Root_Storage_Pool_With_Subpools with private;
7/3function Mark (Pool : in out Mark_Release_Pool_Type)
return not null Subpool_Handle;
8/3procedure Release (Subpool : in out Subpool_Handle) renames
Ada.Unchecked_Deallocate_Subpool;
9/3private
10/3type MR_Subpool is new Subpools.Root_Subpool with record
Start : Storage_Count;
end record;
subtype Subpool_Indexes is Positive range 1 .. 10;
type Subpool_Array is array (Subpool_Indexes) of aliased MR_Subpool;
11/4type Mark_Release_Pool_Type (Pool_Size : Storage_Count) is new
Subpools.Root_Storage_Pool_With_Subpools with record
Storage : Storage_Array (0 .. Pool_Size);
Next_Allocation : Storage_Count := 0;
Markers : Subpool_Array;
Current_Pool : Subpool_Indexes := 1;
end record;
12/3overriding
function Create_Subpool (Pool : in out Mark_Release_Pool_Type)
return not null Subpool_Handle;
13/3function Mark (Pool : in out Mark_Release_Pool_Type)
return not null Subpool_Handle renames Create_Subpool;
14/3overriding
procedure Allocate_From_Subpool (
Pool : in out Mark_Release_Pool_Type;
Storage_Address : out System.Address;
Size_In_Storage_Elements : in Storage_Count;
Alignment : in Storage_Count;
Subpool : not null Subpool_Handle);
15/3overriding
procedure Deallocate_Subpool (
Pool : in out Mark_Release_Pool_Type;
Subpool : in out Subpool_Handle);
16/3overriding
function Default_Subpool_for_Pool (Pool : in out Mark_Release_Pool_Type)
return not null Subpool_Handle;
17/3overriding
procedure Initialize (Pool : in out Mark_Release_Pool_Type);
18/3-- We don't need Finalize.
19/3end MR_Pool;
20/3package body MR_Pool is
21/3use type Subpool_Handle;
22/3procedure Initialize (Pool : in out Mark_Release_Pool_Type) is
-- Initialize the first default subpool.
begin
Pool.Markers(1).Start := 1;
Subpools.Set_Pool_of_Subpool
(Pool.Markers(1)'Unchecked_Access, Pool);
end Initialize;
23/3function Create_Subpool (Pool : in out Mark_Release_Pool_Type)
return not null Subpool_Handle is
-- Mark the current allocation location.
begin
if Pool.Current_Pool = Subpool_Indexes'Last then
raise Storage_Error; -- No more subpools.
end if;
Pool.Current_Pool := Pool.Current_Pool + 1; -- Move to the next subpool
24/3return Result : constant not null Subpool_Handle :=
Pool.Markers(Pool.Current_Pool)'Unchecked_Access
do
Pool.Markers(Pool.Current_Pool).Start := Pool.Next_Allocation;
Subpools.Set_Pool_of_Subpool (Result, Pool);
end return;
end Create_Subpool;
25/3procedure Deallocate_Subpool (
Pool : in out Mark_Release_Pool_Type;
Subpool : in out Subpool_Handle) is
begin
if Subpool /= Pool.Markers(Pool.Current_Pool)'Unchecked_Access then
raise Program_Error; -- Only the last marked subpool can be released.
end if;
if Pool.Current_Pool /= 1 then
Pool.Next_Allocation := Pool.Markers(Pool.Current_Pool).Start;
Pool.Current_Pool := Pool.Current_Pool - 1; -- Move to the previous subpool
else -- Reinitialize the default subpool:
Pool.Next_Allocation := 1;
Subpools.Set_Pool_of_Subpool
(Pool.Markers(1)'Unchecked_Access, Pool);
end if;
end Deallocate_Subpool;
26/3function Default_Subpool_for_Pool (Pool : in out Mark_Release_Pool_Type)
return not null Subpool_Handle is
begin
return Pool.Markers(Pool.Current_Pool)'Unchecked_Access;
end Default_Subpool_for_Pool;
27/3procedure Allocate_From_Subpool (
Pool : in out Mark_Release_Pool_Type;
Storage_Address : out System.Address;
Size_In_Storage_Elements : in Storage_Count;
Alignment : in Storage_Count;
Subpool : not null Subpool_Handle) is
begin
if Subpool /= Pool.Markers(Pool.Current_Pool)'Unchecked_Access then
raise Program_Error; -- Only the last marked subpool can be used for allocations.
end if;
28/4-- Check for the maximum supported alignment, which is the alignment of the storage area:
if Alignment > Pool.Storage'Alignment then
raise Program_Error;
end if;
-- Correct the alignment if necessary:
Pool.Next_Allocation := Pool.Next_Allocation +
((-Pool.Next_Allocation) mod Alignment);
if Pool.Next_Allocation + Size_In_Storage_Elements >
Pool.Pool_Size then
raise Storage_Error; -- Out of space.
end if;
Storage_Address := Pool.Storage (Pool.Next_Allocation)'Address;
Pool.Next_Allocation :=
Pool.Next_Allocation + Size_In_Storage_Elements;
end Allocate_From_Subpool;
29/3end MR_Pool;