Version 1.2 of ais/ai-00162.txt

Unformatted version of ais/ai-00162.txt version 1.2
Other versions for file ais/ai-00162.txt

!standard 03.10.02 (13)          98-10-02 AI95-00162/02
!class binding interpretation 96-09-10
!status work item 98-10-02
!status received 96-09-10
!priority Medium
!difficulty Hard
!subject Anonymous access types and tasks termination/controlled type finalization
!summary 98-10-02
A new master is defined which is associated with the completion of one or more subprogram calls, in a statement or a declaration, that have at least one formal access parameter whose actual parameter is an allocator. This new master is responsible for one or more controlled objects or task objects created by anonymous allocators which are assigned to formal access parameters of one or more subprogram calls in the statement or declaration.
Such a statement or declaration may execute one or more such masters. An
implementation is permitted to determine how many such masters such a statement or declaration will execute.
!question 98-10-02
When an anonymous allocator, which is assigned to a formal access parameter, creates a task or creates a controlled object, who is the master of these things? More specifically, which construct is responsible for waiting for the task to terminate and for finalizing the task or the controlled object?
Here is an example of the problem:
with Ada.Finalization; use Ada.Finalization; package P is task type T; type CT is new Controlled with private; private ... end P;
with P; use P; procedure Test is procedure R (X: access T); procedure S (Y: access CT);
procedure Q (I: Integer) is begin -- (1) ... R (X => new T); -- (2a) ... S (Y => new CT); -- (2b) ... end Q;
procedure R (X: access T) is -- (3a) begin ... end R; procedure S (X: access CT) is -- (3a) begin ... end S;
begin ... Q (I); ... end Test;
Is the master of the anonymous allocator in either statement (2a or 2b) [new T or new CT]: - the innermost enclosing (normal) master (1), - some construct associated with the innermost enclosing statement (2a or 2b)
[as described by 7.6.1(13)] or
- the called subprogram's body (3a or 3b)?
!recommendation 98-10-02
(See summary.)
!wording 98-10-02
There are three wording changes proposed. The first wording change is to the second sentence of 7.6.1(3) to introduce the new master:
"Leaving an execution happens immediately after its completion, except in the case of a master: the execution of a task body, a subprogram_body, an entry_body and certain statements and the elaboration of certain declarations. The execution of a statement is the execution of a master if the statement is a block_statement or an accept statement. If a statement contains one or more anonymous allocators, then the execution of one or more parts of that statement which contain subprogram calls with anonymous allocators is the execution of one or more masters. If a declaration contains one or more anonymous allocators, the elaboration one or more parts of that declaration which contain subprogram calls with anonymous allocators is the execution of one or more masters. An anonymous allocator is an allocator that is the actual parameter in a subprogram call whose associated formal parameter is an access parameter."
The second wording change is to 3.10.2(13) to account for the accessibility level of the allocator:
"The accessibility level of the anonymous access type of an access parameter is the same as that of the view designated by the actual. If the actual is an allocator, this is the accessibility level of the execution of the immediately enclosing statement or the elaboration of the immediately enclosing declaration (which is itself a master -- see 7.6.1)."
The third wording change is to an Implementation Permission paragraph to subsection 7.6.1:
"Implementations have the permission to determine how many actual masters are executed in the execution of a statement or the elaboration of a declaration that causes the execution of multiple subprogram calls with anonymous allocators."
With the new wording for master, there is no need to change 7.6.1(13) to account for anonymous allocators since 7.6.1(4) will handle the task waiting and finalization for the "new" master.
!discussion 98-10-02
As the question states, there are three possibilities for the master of the task or a controlled object created by the anonymous allocator: 1) the innermost enclosing (normal) master 2) some construct associated with the innermost enclosing statement or
declaration
3) the called subprogram's body.
Let's examine each alternative, starting with innermost enclosing (normal) master. While this requires minimal (no) change to the language, it is clumsy in the presence of loops. If the anonymous allocator is inside a loop, one clearly wants to recover the storage associated with the anonymous allocator before going on to the next loop iteration. This would require a block statement inside the loop just for encapsulating the statement with the subprogram call.
The second alternative, some construct associated with the innermost enclosing statement, would introduce one of two options: a) introduce a new master, namely a subprogram call with an anonymous
allocator
b) introduce a new "blocking point" in the execution of a statement
containing a subprogram call with an anonymous allocator
Let's examine the first option, introducing a new master. The following is a proposed wording change to 7.6.1(3) to introduce the new master:
"Leaving an execution happens immediately after its completion, except in the case of a master: the execution of a task body, a subprogram_body, an entry_body and certain statements and the elaboration of certain declarations. The execution of a statement is the execution of a master if the statement is a block_statement or an accept statement. If a statement contains one or more anonymous allocators, then the execution of one or more parts of that statement which contain subprogram calls with anonymous allocators is the execution of one or more masters. If a declaration contains one or more anonymous allocators, the elaboration one or more parts of that declaration which contain subprogram calls with anonymous allocators is the execution of one or more masters. An anonymous allocator is an allocator that is the actual parameter in a subprogram call whose associated formal parameter is an access parameter."
An additional word change is needed in 3.10.2(13) to account for the accessibility level of the allocator:
"The accessibility level of the anonymous access type of an access parameter is the same as that of the view designated by the actual. If the actual is an allocator, this is the accessibility level of the execution of the immediately enclosing statement or the elaboration of the immediately enclosing declaration (which is itself a master -- see 7.6.1)."
If the new wording for master is used, then it appears that there is no need to change 7.6.1(13) to account for anonymous allocators since 7.6.1(4) will handle the "new" master. Some might argue this change as too strong, where pretty much anything is a master, and the concept loses its intuitive appeal. The counter argument is that subprogram calls with anonymous allocators represent a (very) small subset of subprogram calls.
This wording also appears to be unaffected by what statement or declaration the subprogram call with an anonymous allocator might appear. And the only affect it appears to have on the context where it is used is to add more time to the execution of the enclosing statement or the elaboration of the enclosing declaration. In particular, their appearance in a barrier condition are handled by the existing rules on entry calls, namely, such an anonymous allocator can not allocate a task object because that would violate 9.5.1(14) which prevents task activation.
The second option, introducing a new "blocking point", would leave the enclosing block as the master of the task created by the anonymous allocator. Instead this option would add words to 7.6.1(13) which says that any tasks created by an anonymous allocator in a subprogram call are waited for before proceeding to the next statement. Some might argue that this introduces yet another exception to the rules on master-task dependency. But more importantly, requiring that the task be awaited before going on to the next statement without considering the statement itself a master, we will get in trouble with the "terminate" alternative rules, which require that a task depend on some "completed master" for collective termination to kick in.
The third alternative would make the called subprogram's body the master of the task or controlled object created by the anonymous allocator. This is based on the concept the formal parameters of a subprogram should be considered locals of the subprogram, with the actual parameters being the initial expressions for the formals. With this idea, the conceptual "frame" for the called subprogram is created at the start of the evaluation of the actuals, rather than after the evaluation is complete. When the subprogram body returns, it must wait for the task to complete before returning, as usual. This model matches the wording in 3.10.2(13) pretty well. As a favor to implementors, an Implementation Permission would be added where an implementation may postpone waiting for such tasks until other finalization associated with other anonymous objects created by the subprogram call is performed (as defined by 7.6.1(13)). To support this alternative, at least two parts of the ARM that may be affected by wording change: 6.4(10) which describes the overall dynamic semantics of a subprogram call and 6.4.1(8,9) which describe the semantics of parameter association evaluation. 6.4(10) is very clear that the parameter association evaluations are done outside and separately from the execution of the subprogram body. This is inconsistent with the normal relationship of a master and the resources that are dependent on the master. It appears that this paragraph will need major changes. If that is true, what other parts of the ARM would be affected by this change? For example, isn't the dynamic semantics of parameter association evaluation for subprogram calls used as the model for entry calls and protected subprogram calls. Does this mean that the protected subprogram body is blocked waiting for the task to complete? I don't think so. This alternative is beginning to appear very messy.
It appears that the first option of the second alternative is best choice.
Assuming the first option of the second alternative is selected, implementations should have the permission to determine how many masters to execute in the presence of multiple subprogram calls with anonymous allocators, as this example should illustrate:
with P; use P; procedure Test2 is function F (M: access T) return Boolean; function G (N: access T) return Boolean; procedure H (I, J: Boolean); ... begin ... H (F (new CT), G (new CT)); -- (4) ... end Test2;
In the statement (4), the implementation should be permitted to execute one master, associated with the completion of the call to H, or to execute two masters, one associated with the completion of F and the other associated with the completion of G. The following proposed Implementation Permission paragraph should be added to subsection 7.6.1:
"Implementations have the permission to determine how many actual masters are executed in the execution of a statement or the elaboration of a declaration that causes the execution of multiple subprogram calls with anonymous allocators."
!appendix

!section 3.10(2)
!subject Anonymous access types and finalization
!reference RM95 3.10(2)
!reference RM95 3.7(27)
!reference RM95 7.6.1(11)
!from Pascal Leroy 96-08-20
!reference 96-5633.a Pascal Leroy 96-8-21>>
!discussion

The finalization of an object created by an allocator occurs when the master
of the ultimate ancestor of the access type is left, as stated in 7.6.1(11).
 In the case of access discriminants, 3.10(2) tells us that "an
access_definition defines an anonymous access type"; I take this to imply that
an anonymous access type is an "ultimate ancestor" in the sense of 7.6.1(11).
 In addition, 3.7(27) tells us that "an access_definition is elaborated when
the value of a corresponding access discriminant is defined"; I take this to
imply that the master of the anonymous access type is the master that
elaborated the discriminant constraint.

The interaction of these rules has the unfortunate consequence that it makes
it possible to reference an object after it has been finalized, as shown in
the following example:

    procedure P1 is
        type T (D : access Some_Controlled_Type) is ... ;

        procedure P2 is
            type T_Ref is access T;

            X : T_Ref;

            procedure P3 is
            begin
                X := new T (D => new Some_Controlled_Type);
                -- (1)
            end P3;
        begin
            P3;
            -- (2)
        end P2;
    begin
        P2;
    end P1;

The discriminant constraint for the allocated object X.all is elaborated by
P3.  The elaboration of this constraint also elaborates the access_definition,
therefore, the master of the anonymous access type is P3.  So when leaving P3,
at the point marked (1), the allocated object X.all.D.all is finalized, as per
7.6.1(11).  But of course, after leaving P3 we can still use the name
X.all.D.all to reference this object (at the point marked (2)), and this is
bad...

It seems to me that we need a rule that says that the master of an anonymous
access type A used in the specification of an access discriminant in type T,
the master of A is the master of T, not the master that caused A to be
elaborated.  Or, alternatively, that when an allocator is used as a
discriminant value for an anonymous access type, the master of the allocated
object is defined to be the master of the discriminated object. (These two
proposals are different, but they both seem to cure the problem, although the
first one is probably easier to implement.)

_____________________________________________________________________
Pascal Leroy                                    +33.1.30.12.09.68
pleroy@rational.com                             +33.1.30.12.09.66 FAX

****************************************************************

!section 3.10(2)
!subject Anonymous access types and finalization
!reference RM95 3.10(2)
!reference RM95 3.7(27)
!reference RM95 7.6.1(11)
!reference RM95 3.10.2(12)
!reference 96-5633.a Pascal Leroy 96-08-20
!from Tucker Taft 96-08-22
!reference 96-5637.a Tucker Taft 96-8-22>>
!discussion

> The finalization of an object created by an allocator occurs when the master
> of the ultimate ancestor of the access type is left, as stated in 7.6.1(11).
>  In the case of access discriminants, 3.10(2) tells us that "an
> access_definition defines an anonymous access type"; I take this to imply that
> an anonymous access type is an "ultimate ancestor" in the sense of 7.6.1(11).
>  In addition, 3.7(27) tells us that "an access_definition is elaborated when
> the value of a corresponding access discriminant is defined"; I take this to
> imply that the master of the anonymous access type is the master that
> elaborated the discriminant constraint.

Not quite.

The accessibility level determines the lifetime for the purposes of
finalization.  For access discriminants, the accessibility level is
defined by RM95-3.10.2(12):

    The accessibility level of the anonymous access type of an access
    discriminant is the same as that of the containing object or associated
    constrained subtype.

> The interaction of these rules has the unfortunate consequence that it makes
> it possible to reference an object after it has been finalized, as shown in
> the following example:

This is not a problem, given 3.10.2(12).

>     procedure P1 is
>         type T (D : access Some_Controlled_Type) is ... ;
>
>         procedure P2 is
>             type T_Ref is access T;
>
>             X : T_Ref;
>
>             procedure P3 is
>             begin
>                 X := new T (D => new Some_Controlled_Type);
>                 -- (1)
>             end P3;
>         begin
>             P3;
>             -- (2)
>         end P2;
>     begin
>         P2;
>     end P1;
>
> The discriminant constraint for the allocated object X.all is elaborated by
> P3.  The elaboration of this constraint also elaborates the access_definition,
> therefore, the master of the anonymous access type is P3.  So when leaving P3,
> at the point marked (1), the allocated object X.all.D.all is finalized, as per
> 7.6.1(11).  But of course, after leaving P3 we can still use the name
> X.all.D.all to reference this object (at the point marked (2)), and this is
> bad...
>
> It seems to me that we need a rule that says that the master of an anonymous
> access type A used in the specification of an access discriminant in type T,
> the master of A is the master of T, not the master that caused A to be
> elaborated.  Or, alternatively, that when an allocator is used as a
> discriminant value for an anonymous access type, the master of the allocated
> object is defined to be the master of the discriminated object. (These two
> proposals are different, but they both seem to cure the problem, although the
> first one is probably easier to implement.)

As specified by 3.10.2(12), the accessibility level associated with the
nested allocator "new Some_Controlled_Type" is the same as that of
the enclosing object, X.all, which is the same as T_Ref.  The master
is hence P2, and the finalization for the nested allocator should be
performed when exiting P2.

> Pascal Leroy                                    +33.1.30.12.09.68
> pleroy@rational.com                             +33.1.30.12.09.66 FAX

-Tuck

****************************************************************

!section 3.10(2)
!subject Anonymous access types and finalization
!reference RM95 3.10(2)
!reference RM95 3.7(27)
!reference RM95 7.6.1(11)
!reference RM95 3.10.2(12)
!reference 96-5633.a Pascal Leroy 96-08-20
!from Pascal Leroy 96-08-23
!reference 96-5641.a Pascal Leroy 96-8-23>>
!discussion

> The accessibility level determines the lifetime for the purposes of
> finalization.  For access discriminants, the accessibility level is
> defined by RM95-3.10.2(12):
>
>     The accessibility level of the anonymous access type of an access
>     discriminant is the same as that of the containing object or associated
>     constrained subtype.

I stand corrected.  I had failed to see the significance of accessibility
levels in this case, as explained in 7.6.1(4).  I guess I was reasoning a la
Ada 83 ;-)

Pascal

_____________________________________________________________________
Pascal Leroy                                    +33.1.30.12.09.68
pleroy@rational.com                             +33.1.30.12.09.66 FAX

****************************************************************

!section 3.10.2(13)
!subject Anonymous allocators and tasks/finalization
!reference RM95 3.10.2(13)
!reference RM95 7.6.1(13)
!reference 96-5633.a Pascal Leroy 96-08-20
!reference 96-5637.a Tucker Taft 96-08-22
!reference 96-5641.a Pascal Leroy 96-08-20
!from Tucker Taft 96-09-10
!reference 96-5698.a Tucker Taft 96-9-10>>
!discussion

In 96-5633.s Pascal Leroy indicated a concern with when the object
created by an "anonymous" allocator (one whose expected type
was the anonymous access type of an access parameter) would
be finalized.  In answer, I pointed to 7.6.1(13) where it identifies
the accessibility level of such allocators as being the same as
that of the execution of the called subprogram.

That seemed to resolve the issue.  However, if you consider
anonymous allocators that create tasks as part of an object
whose size is not known at compile-time, you encounter the
first situation where the master of a task should perhaps be the
execution of a construct "smaller" than a block statement.

It seems that 7.6.1(13) should be augmented to cover the case
of anonymous allocators, which can result in the creation of
"anonymous" objects as part of any subprogram call.  Since these
anonymous objects might be of a limited type (unlike the case
for function returns and array aggregates), we need to consider
task "finalization" as well.  I believe the only consistent
approach is to consider the "master" for such anonymous objects
to be (the execution of) the construct (or construct "part") containing
the subprogram call, and ensure that both task waiting and finalization
occur as part of "leaving" the construct part.  By construct part, I mean
what 7.6.1(13) implicitly identifies as the unit of finalization for temps,
namely, the innermost declarative_item or statement, or in the case of
a compound statement, the part of the compound statement
up to the next statement nested within the compound statement.

Two (rejected0 alternatives are to associate the "anonymous" task with the
innermost enclosing (normal) master of the caller, or with the
execution of the called subprogram.  Neither of these work too well.

    Associating with the enclosing master doesn't work,
    since if the anonymous allocator is inside a loop, one clearly wants
    to recover the storage associated with the anonymous allocator
    before going on to the next loop iteration, even though a loop
    is not a master.

    Associating with the called subprogram
    doesn't work too well, since the call frame for the called
    subprogram doesn't exist at the time when the anonymous task is
    created (and may never exist if one of the later parameter
    evaluations raises an exception).

Also, other temps associated with subprogram call are finalized
immediately after the results of the call are no longer needed, so
it seems that anonymous allocators should operate the same way.

Hence, it seems we need a new notion of "master" to properly
deal with anonymous allocators, or we at least need to *permit*
implementations to wait for tasks created by anonymous allocators
before the enclosing "normal" master completes (I suppose non-real-time
implementations can wait for anything anytime they want, but we care
about real-time-conforming implementations as well).

In any case, 7.6.1(13) should acknowledge this new kind of anonymous
object, namely the anonymous object created by the evaluation
of an "anonymous" allocator.

-Tuck

****************************************************************

Questions? Ask the ACAA Technical Agent