AI22-0162-1

!standard 13.11.4(33)                                     26-07-20  AI22-0162-1/03

!standard 13.11.5(3)

!standard 13.11.5(5)

!standard 13.11.5(10)

!class Binding Interpretation 26-06-06

!status work item 26-06-06

!status received 26-06-06

!assigned author Brad Moore

!submitter Brad Moore

!priority Medium

!difficulty Easy

!subject Storage Subpool Reclamation

!summary

We clarify that the actions performed for a storage subpool for a call to Unchecked_Deallocate_Subpool are reclamation actions, not finalization. We further specify that finalization for the type Root_Storage_Pool_Subpools is not required to finalize storage pool objects.

!issue

The actions that occur for a call to Unchecked_Deallocate_Subpool for a storage subpool can be found under 13.11.5(5). These effects are described as being finalization actions, yet this really isn’t finalization in the language sense, which can be confusing to the reader. These actions include a call to Deallocate_Subpool, which is a user-defined procedure. An implementation of Deallocate_Subpool might simply contain a call to Unchecked_Deallocation on the subpool object, which could be considered as “finalizing” the designated subpool object. Is it possible that a call to Unchecked_Deallocation could trigger these finalization actions for a storage subpool in a recursive manner? (No.)

A bigger issue can be found in 13.11.5(10/3), where it states that finalization of a Root_Storage_Pool_With_Subpools object finalizes all subpools that belong to that pool that have not yet been finalized. Is this referring to the finalization of subpool objects? (No, it is referring only to subpools, which are the subpool handles that designate subpool objects.) Yet, 13.11.5(8.3) specifies that Deallocate_Subpool is called as part of the steps of finalizing/reclaiming a subpool. That would imply somehow keeping track of all of the subpools (which are handled) in order to call Deallocate_Subpool as would be required. But bare handles (as these are intended to be) can’t easily be tracked by the implementation, and moreover, we don’t want to “finalize” copies of handles that have already been “finalized”. Additionally, Deallocate_Subpool is about recovering memory for future use by a pool, which is unnecessary if the pool is going away anyway. Thus, it appears that 13.11.5(10/3) is over-specified; a programmer could override the Finalize routine for the pool if they really need to do something when the pool goes away. Should we remove the burden of having to track subpool handles for the purpose of having the implementation call Deallocate_Subpool when an object of Root_Storage_Pool_With_Subpools is finalized? (Yes.)

!recommendation

The confusion from the first issue appears to stem from the use of the term, finalization, in 13.11.5(4/3) and 13.11.5(5/5), when these actions do not fit the language definition of finalization.

To address the first issue, the recommendation is to replace the use of the term “finalized” in 13.11.5(4/3) and 13.11.5(5/5) with a different term to avoid this confusion. Given that the title for 13.11.5 is Subpool Reclamation, it naturally follows that “reclaimed” would be a good choice for a replacement.

To address the second issue, the recommendation is to separate the effects of calling Deallocate_Subpool and the following effect stating that the subpool ceases to belong to any pool when Unchecked_Deallocate_Subpool is called, from the effects that describe reclaiming a subpool. Instead, the call to Deallocate_Subpool is specified to be issued after the subpool has been reclaimed, as part of the call to Unchecked_Deallocate_Subpool, and thus is not considered to be an effect of finalizing a Root_Storage_Pool_With_Subpools object. To further clarify this, an AARM note is added after 13.11.5(10/3) to explain this better.

!wording

Add after 13.11.4(31.1/4):

If an extension of Root_Storage_Pool_With_Subpools overrides the Finalize routine, execution is erroneous when that Finalize routine is called and it does not start by calling the parent Finalize routine.

AARM Reason: The definition of Finalize for type Root_Storage_Pool_With_Subpools most likely handles the required clean-up of subpools (see 13.11.5). If Finalize is overridden and that implementation is not called first, then the requirements of 13.11.5 will not be met. There is no current Ada way to ensure this, so we have to declare the execution erroneous (as we do with any other requirements on user code that cannot be enforced).

Add after 13.11.4(32/3):

Implementation Permission

During the finalization of a Root_Storage_Pool_With_Subpools object, dispatching calls to Deallocate_Subpool may be issued for subpools belonging to the pool object.

AARM Discussion: This could be used to provide backwards compatibility for previously existing storage subpool implementations that relied on the implementation to provide all needed finalization for the pool object and its subpool objects designated by its associated subpools.

Modify 13.11.5(4/3):

If Subpool is null, a call on Unchecked_Deallocate_Subpool has no effect. Otherwise, the subpool is {reclaimed}[finalized], and Subpool is set to null.

Modify 13.11.5(5):

           [Finalization of] {Reclaiming} a subpool has the following effects in the given order:

Delete 13.11.5(8/3):

Delete 13.11.5(9/3):

Delete 13.11.5(9.1/3):

Add after 13.11.5(9.1/5): (Not-part of the bulleted list)

Following the reclamation of the subpool, the following[Redundant: dispatching[a][b]] call is then made prior to returning from a call to Unchecked_Deallocate_Subpool:

   Deallocate_Subpool(Pool_of_Subpool(Subpool).all, Subpool);

Modify 13.11.5(10/3):

Finalization of a Root_Storage_Pool_With_Subpools object [finalizes]{reclaims} all subpools that belong to that pool that have not yet been [finalized]{reclaimed}.

Add after 13.11.5(10/3):

AARM Note: Finalization of a Root_Storage_Pool_With_Subpools object only reclaims subpools (see 13.11.4(18/3)), and does not finalize subpool objects (see 13.11.4(19/5)), unless via dispatching calls to Deallocate_Subpool. The finalization of any remaining subpool objects associated with a pool object derived from Root_Storage_Pool_With_Subpools is ultimately the responsibility of the subpool implementor.

!discussion

Objects of the language-defined Subpool_Handle type defined in 13.11.4(6/3) are used to manage storage subpools. Users of storage pools that support subpools can call Unchecked_Deallocate_Subpool to reclaim the storage associated with a subpool.

The actions that occur for a call to Unchecked_Deallocate_Subpool are listed after 13.11.5(5/5).

These actions only happen during an explicit call to Unchecked_Deallocate_Subpool and when the pool containing the subpool is finalized (as described in 13.11.5(10/3). A "subpool" itself is a handle that is not finalized (or more accurately, the finalization has no effect).

13.11.4(18/3) defines a “subpool” as an object of type Subpool_Handle. The object that the handle points at is called a “subpool object” in that same section.

One of the listed actions of 13.11.5(5) is to issue a dispatching call to the user-defined Deallocate_Subpool primitive procedure. That routine might (and probably will) call Unchecked_Deallocation on the subpool object. A call on Unchecked_Deallocation will finalize the subpool object that the subpool handle designates. But the subpool object is not itself a subpool (as that is a handle!), and thus none of the actions of 13.11.5 are associated with that call to Deallocate_Subpool.

As such, there is no possibility of a “loop”. Finalizing a subpool object does not finalize any “subpools”.  Note again that subpools are handles, so they themselves do not get finalized; rather, “finalization” of a subpool happens either as part of a call to Unchecked_Deallocate_Subpool, or as part of the finalization of the pool that they belong to (see 13.11.5(10/3)). Neither of these has anything to do with the finalization of any subpool objects (that is, the thing that is designated by a subpool [handle]).

Although a Subpool_Handle is an access type that designates a storage subpool, it is illegal to call Unchecked_Deallocate on a Subpool_Handle because the Storage_Size attribute for this type is specified to be zero. See 13.11.2(3.1/3). A storage pool implementor that needs to internally deallocate the memory associated with a subpool can get around this limitation by converting a Subpool_Handle object to another access type that can designate the subpool for which the Storage_Size attribute is not specified to be zero. This is a typical approach used in storage subpool implementations.

The wording change makes it clearer that the listed actions of calling Unchecked_Deallocate_Subpool are not finalization actions, but rather storage reclamation actions.

A secondary question was raised about the effect of calling Unchecked_Deallocate_Subpool on a subpool that does not belong to a storage pool.

We already require Pool_of_Subpool to return null if Set_Pool_of_Subpool is not set, so a call to Unchecked_Deallocate_Subpool necessarily will raise Constraint_Error when Deallocate_Subpool is called. As this represents a bug (Create_Subpool is supposed to call Set_Pool_of_Subpool), steps 1 and 2 can do nothing, as no objects could have been allocated from such a subpool. So this case is adequately handled. For the case of a subpool that is null, no finalization should be done.

Similarly, the case when Allocate_From_Subpool is called with a subpool that designates some other pool than the one passed to Allocate_From_Subpool (including designating no pool) is covered by having the class-wide precondition for that call to do the needed check (See 13.11.4 (11/5)).

As mentioned above, 13.11.5(10/3) now says that all subpools are “reclaimed” when the pool object is finalized.

The final effect (13.11.5(9.1/3) stating that the subpool does not belong to any pool is not something that the implementation can guarantee, because it cannot guarantee that the subpool implementor doesn’t have copies of the subpool handle in its pool object implementation.

Typically, it is the call to Deallocate_Subpool that would finalize the subpool object, and remove any subpool handles that designated that subpool object. Furthermore, it wouldn’t make sense for the subpool implementation to override Finalize and free any subpool objects if it really were true that subpool handles ceased to belong to any pool upon returning from the parent call to Finalize, prior to doing any necessary cleanup by the storage pool implementation.

It is the call to Deallocate_Subpool that can ensure that the subpool no longer belongs to the pool. Otherwise, the implementation cannot guarantee that claim, which is why we delete 13.5.11(9.1/3), since that is really the responsibility of the subpool implementor.

The implementation may call Deallocate_Subpool for all active subpools associated with the pool as part of the finalization of a Root_Storage_Pool_With_Subpools object, for backwards compatibility, but is not required to do so.

!ACATS test

An ACATS C-Test should show that the Finalize of a Root_Storage_Pool_With_Subpools object does not finalize any of the subpool objects associated with the pool without having called Deallocate_Subpool for such subpool objects.

!appendix

This is related to  ARG GitHub issue #170.

[a]What is the significance of the square brackets?  It can't be a deletion since it's in an add paragraph,

[b]I think it is supposed to be marked as Redundant. I've made that change, but it might be best to check that when we review it tomorrow.