Version 1.2 of ai05s/ai05-0252-1.txt

Unformatted version of ai05s/ai05-0252-1.txt version 1.2
Other versions for file ai05s/ai05-0252-1.txt

!standard 13.11.4(0)          11-06-16 AI05-0252-1/01
!standard 13.11.5(0)
!class Amendment 11-06-16
!status work item 11-06-16
!status received 11-05-13
!priority Low
!difficulty Medium
!subject Questions on subpools
!summary
**TBD.
!question
(1) Bob had asked why Subpool_Handles are nulled out by
an Unchecked_Deallocate_Subpool. This would be easy to change, so we ought to think about it.
(2) Tucker's comments on the letter ballot reminded the editor that there should
not be anything normative in the comments on the routines in the Subpools package. All of this information was moved elsewhere; much of it was in Tucker's new user Notes (added at the bottom of 13.11.4); a few items were moved to AARM notes; and the rest became new paragraphs in Static Semantics.
Since these were extensive changes (including the addition of Tucker's user Notes), it would be good if all ARG members reviewed 13.11.4 in the current Standard draft.
(3) If a pool is passed as a parameter, and that pool is used for a local access
type, the accessibility check will pass, but the pool will belong to a different master than the access type. (See e-mail of June 19th.)
!proposal
** TBD.
!wording
** TBD.
!discussion
For (1), the model of AI05-0111-3 is that subpools are not logically reused. That is, a subpool is created, used a while, then deallocated. If you create another subpool after that, you get a different logical subpool, even though implementations are allowed to reuse the subpool objects (and thus the handle value could be the same). Since there is no reuse of subpools (logically), nulling the handle after use makes sense. (It also is similar to what Unchecked_Deallocation does.)
We could adopt a different model instead where subpools can be reused (although it would be completely up to pool implementers to provide such operations). In that case, it would make sense to not null out subpool handles (so they are ready for reuse if desired). To reuse a subpool, it still would necessary to call Set_Pool_for_Subpool (as that routine also reinitializes the data structures necessary to handle finalization).
(3) seems to require a purpose-built legality rule or runtime check. Such a parameter could never pass the accessibility check for a local access type (and it wouldn't be visible for any other kind of access type), so we can ban it outright. But we would have to cover Root_Storage_Pool'Class as well, and that would be somewhat incompatible.
===========================
Bob asked four additional questions in his letter ballot comments. (Actually six, but one depends on another that is unlikely to be changes, and another actually was something that Bob himself had changed in a previous draft.) These don't seem worth considering now; here's the questions and the editor's replies:
[A] Why do we have to do everything via handles anyway? What's wrong with
creating a subpool on the stack, and let it be deallocated upon procedure return?
That way lies madness. :-) The AI model requires all subpools to "belong" to the pool, and it is the pool implementer's responsibility to ensure the objects live as long as the pool (unless deallocated). If this is violated, the normal dangling pointer rules cover any problems (and these occur mostly from are mistakes by the pool implementer - caused by premature deallocation of subpool objects; it is highly unlikely that use of deallocated subpool handles by clients will not be detected, and pool implementers can reduce that to zero).
In particular, accessibility checks ensure that the pool (and thus any subpool objects) live as long as any access types using the pool.
However, if the subpool objects are managed by the client rather than the pool implementer, then dangling subpools become much more common and the pool implementer cannot do anything to prevent problems. Moreover, we now have problems because access types can contain subpools whose object have to disappear before the type.
[B] The idea that "new" can call either Allocate or Allocate_From_Subpool,
depending on compiler-writer's whim, seems kludgy to me. The reason for this kludge is that Bob insisted on doing it one way, and Randy insisted on the other way.
Bob is clearly wrong. ;-) Requiring "new" to call Allocate_From_Subpool (in the rare case that whether the pool supports subpools is unknown) is less error-prone (it is much less likely that a user will override hidden routines, especially when the parameter types are also hidden, than a visible routine like Allocate in Subpools), it's more flexible (in that Allocate_From_Subpool can always be called for any access type in this model), and it works just fine with the existing Ada inheritance rules.
But I don't think it is worth having this argument again (unless Bob is ready to give in).
[C] Objects in subpools should be finalized when the pool is finalized (if not
done earlier), not when the access type collection is finalized. It seems really weird that this is an Implementation Permission, rather than a hard requirement.
Making this a hard requirement would be insisting that they live (slightly) longer than currently required. That also seems weird. Moreover, depending on the implementation model, it may be the case that it is easier to finalize the collections rather than the subpools. Given that these finalizations have to be fairly close together, it doesn't seem worthwhile to insist on one or the other.
Absent any evidence that no implementation could ever take advantage of this permission, it seems harmless to keep it and possibly valuable.
[D] There might be a better answer than "raise Program_Error" when
tasks are allocated in subpools. But Bob can't determine that without some serious study of our [AdaCore's] runtimes. And Randy has completely a opposing viewpoint on this.
I'm not sure what is opposing about my viewpoint; my personal viewpoint is that task termination is insufficiently flexible for all tasks (immediate termination should be supported) and we ought to consider something that applies to all tasks (perhaps an aspect) rather than futzing with this one place.
The original rules here came from Tucker, not me, and I've mostly tried to bend them into something workable. And it was the ARG as a whole that requested completely removing the concept of dynamic masters -- I actually agree with Bob that those would be preferable to most of these other rules -- but I saw no evidence that the rest of the ARG would go along.
In any case, significant change in this area requires both prototyping in various compiler runtimes and willingness of the ARG to adopt a more complex solution -- the first cannot be done at this meeting and the latter is unlikely.
!ACATS test
** TBD.
!appendix

From: Bob Duff
Sent: Friday, May 13, 2011  9:26 AM

I wrote:

> AI05-0111-3/10  Subpools, allocators, and control of finalization
>    Approve ______ Disapprove ___X___ Abstain _______

I reluctantly decided to vote NO on subpools.  I'm thinking this AI has bugs,
and it's premature to approve it.  And Randy doesn't want any further technical
discussion at this point.  So it seems more appropriate for AdaCore to implement
it, and work out the details during that implementation, and then maybe it can
be standardized in Ada 2020.

Whether it gets approved or not, I think AdaCore is probably going to go ahead
with implementation.

Things I think are questionable (just for the record -- I'm not asking anyone to
discuss these now):

    Nulling out of Subpool_Handles.  The AI seems to say it's
    OK to reuse subpools, so why null them out?  Anyway, if
    dangling pointers are an issue, it's dangling pointers to
    objects allocated in subpools, not (so much) dangling
    Subpool_Handles.

    Why do we have to do everything via handles anyway?
    What's wrong with creating a subpool on the stack,
    and let it be deallocated upon procedure return?

    And in that case, why isn't Root_Subpool controlled?

    The idea that "new" can call either Allocate or Allocate_From_Subpool,
    depending on compiler-writer's whim, seems kludgy to me.  The reason for
    this kludge is that I insisted on doing it one way, and Randy insisted on
    the other way.

    Objects in subpools should be finalized when the pool is finalized (if not
    done earlier), not when the access type collection is finalized.
    It seems really weird that this is an Implementation Permission,
    rather than a hard requirement.

    Why does Default_Subpool_for_Pool raise Storage_Error if there is no
    default?  Program_Error (or a more-specific No_Default_Subpool_Error)
    would make more sense.

    There might be a better answer than "raise Program_Error" when
    tasks are allocated in subpools.  But I can't determine that
    without some serious study of our runtimes.  And Randy has
    completely a opposing viewpoint on this.

None of the above are major issues; I just feel like they haven't been thought
through.  And Randy says there's no more time for "thinking through".

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

From: Robert Dewar
Sent: Friday, May 13, 2011  9:31 AM

>> AI05-0111-3/10  Subpools, allocators, and control of finalization
>>     Approve ______ Disapprove ___X___ Abstain _______
>
> I reluctantly decided to vote NO on subpools.  I'm thinking this AI
> has bugs, and it's premature to approve it.  And Randy doesn't want
> any further technical discussion at this point.  So it seems more
> appropriate for AdaCore to implement it, and work out the details
> during that implementation, and then maybe it can be standardized in Ada 2020.

I tend to agree with Bob, given that they will be available in GNAT, it is not
as though we have missed an opportunity here, and it would be unfortunate to set
mistakes in stone.

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

From: Randy Brukardt
Sent: Friday, May 13, 2011  2:45 PM

> Whether it gets approved or not, I think AdaCore is probably going to
> go ahead with implementation.

OK, but it can only be under an extensions switch (as new syntax is involved).
That means that only people willing to be permanently locked into an AdaCore
compiler can use it. One would hope that is a reasonably small group, meaning it
won't have much use.

> Things I think are questionable (just for the record -- I'm not asking
> anyone to discuss these now):
>
>     Nulling out of Subpool_Handles.  The AI seems to say it's
>     OK to reuse subpools, so why null them out?  Anyway, if
>     dangling pointers are an issue, it's dangling pointers to
>     objects allocated in subpools, not (so much) dangling
>     Subpool_Handles.
>
>     Why do we have to do everything via handles anyway?
>     What's wrong with creating a subpool on the stack,
>     and let it be deallocated upon procedure return?

The intent was that subpool objects are logically part of the pool object; that
eliminates the need for separate accessibility checks for them and greatly
reduces the possibilities for dangling handles.

Other models are possible of course, but they would require extensive
accessibility checking -- which was part of why Tucker's proposals were so
overwhelmingly complicated. I didn't think anything with such checking could
ever get approved.

>     And in that case, why isn't Root_Subpool controlled?

Overhead, of course.

>     The idea that "new" can call either Allocate or Allocate_From_Subpool,
>     depending on compiler-writer's whim, seems kludgy to me.  The reason for
>     this kludge is that I insisted on doing it one way, and Randy insisted on
>     the other way.

There is no need for the kludge, it exists because you wouldn't admit that you
were wrong. ;-)

>     Objects in subpools should be finalized when the pool is finalized (if not
>     done earlier), not when the access type collection is finalized.
>     It seems really weird that this is an Implementation Permission,
>     rather than a hard requirement.

Making it a hard requirement means that implementations potentially have to
change more of their implementation. The flexibility seemed worthwhile
(especially as we don't have an implementation of that finalization mechanism
yet). And it shouldn't matter to users (given that the scopes have to be the
same).

>     Why does Default_Subpool_for_Pool raise Storage_Error if there is no
>     default?  Program_Error (or a more-specific No_Default_Subpool_Error)
>     would make more sense.

Makes no difference to me; this can be changed during editorial review.

>     There might be a better answer than "raise Program_Error" when
>     tasks are allocated in subpools.  But I can't determine that
>     without some serious study of our runtimes.  And Randy has
>     completely a opposing viewpoint on this.

I'm not sure what is "opposing" here; I think there is a better answer than
"raise Program_Error", too. I don't see much reason to fix termination issues
here alone, however; if there is a real problem, it ought to be fixed for all
tasks (via additional capabilities, of course).

The fact that tasks don't terminate immediately when completed forces zombie
tasks to live on all sorts of instances. We should consider adding a mechanism
to change that in every instance (rather than just this one).

The other part is "terminate" alternatives, I'd like to see those handled
somehow but that clearly is a separate issue.

> None of the above are major issues; I just feel like they haven't been
> thought through.  And Randy says there's no more time for "thinking
> through".

I think they've been thought through for the model suggested by this AI.

If we want to consider a completely different model (such as the one you
suggested with separate objects or the very similar version that Brad proposed),
that's a different story. Clearly we can't do that in the time remaining.


But I think it is now or never for improving the memory management of Ada. Since
early finalization is not allowed, Ada supports no form of advanced memory
management for any object that might contain a controlled type. (Not only
subpools, but also no garbage collection or anything else semi-automatic.)
That's a huge restriction, meaning no unbounded containers or unbounded strings,
virtually no user-defined "objects", and little ability to use canned libraries.
That's clearly OK for some customers; those customers are unlikely to use
dynamic storage management anyway. But for the rest of us, we're going to remain
in the drak ages for another 6-10 years (using single vendor implementations is
not a realistic option for most - unless of course there is only a single
vendor). By that time, the only customers left will be those that don't want
dynamic memory in the first place, so there will be no value to spending time on
memory management issues.

So, if we decide that we don't want this now, I will oppose any future
consideration of such ideas. This should have been done in Ada 2005, and if we
can't do it in Ada 2012, it simply is too late to bother. We need to focus our
energies on what we can agree on (contracts).

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

From: Bob Duff
Sent: Friday, May 13, 2011  3:22 PM

> OK, but it can only be under an extensions switch (as new syntax is
> involved).

Right, it would have to be controlled by some sort of option.

Anyway, if ARG approves this AI, I won't complain.
I expect there will be some binding interpretations coming along in that case,
which I suppose is OK.

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

From: Robert Dewar
Sent: Friday, May 13, 2011  11:58 PM

> OK, but it can only be under an extensions switch (as new syntax is
> involved). That means that only people willing to be permanently
> locked into an AdaCore compiler can use it. One would hope that is a
> reasonably small group, meaning it won't have much use.

Actually the great majority of our users don't care about this issue, and use
GNAT extensions (pragmas and attributes for one thing) freely, so I don't think
this is an issue, if this feature is useful, people will use it.

> So, if we decide that we don't want this now, I will oppose any future
> consideration of such ideas. This should have been done in Ada 2005,
> and if we can't do it in Ada 2012, it simply is too late to bother. We
> need to focus our energies on what we can agree on (contracts).

That seems a dubious rhetorical device to me, and I don't think anyone
(certainly not me) will take such opposition very seriously if it is based
solely on that reasoning.

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

From: Robert Dewar
Sent: Friday, May 13, 2011  11:59 PM

> OK, but it can only be under an extensions switch (as new syntax is
> involved).

Well yes, although we might for instance decide to have it as the default, and
require some switch to get standard behavior. After all you already have to use
-gnatE -gnato to get standard semantics with GNAT, these are no default modes.

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

From: Randy Brukardt
Sent: Friday, May 13, 2011 12:42 AM

> > OK, but it can only be under an extensions switch (as new syntax is
> > involved).
>
> Well yes, although we might for instance decide to have it as the
> default, and require some switch to get standard behavior. After all
> you already have to use -gnatE -gnato to get standard semantics with
> GNAT, these are no default modes.

You might want to consider including rejecting that if Profile
(No_Implementation_Extensions) has been used -- if the user has indicated that
they don't want implementation pragmas and attributes and all of the other
things, it's pretty unlikely that they would want some non-Ada syntax. (I'm not
sure if doing that is actually allowed by the standard, but it surely should
be.) I've made myself a note to do that in Janus/Ada when that profile is
implemented.

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

From: Randy Brukardt
Sent: Friday, May 13, 2011 12:49 AM

..
> (I'm not sure if doing that is actually allowed by the standard, but
> it surely should be.) I've made myself a note to do that in Janus/Ada
> when that profile is implemented.

Actually, this isn't a problem for the standard, because a non-standard mode can
do whatever it wants (including alter the action of a pragma Profile). If the
extensions switch(es) aren't used, the pragma Profile action follows the
standard exactly (but then there are no syntax extensions anyway).

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

From: Randy Brukardt
Sent: Sunday, June 12, 2011  11:23 PM

...
>     Why does Default_Subpool_for_Pool raise Storage_Error if there is no
>     default?  Program_Error (or a more-specific No_Default_Subpool_Error)
>     would make more sense.

Recent versions of the AI raise Program_Error. This was changed in version
/09 (April 12) by one Bob Duff.

So you're complaining about something that was already changed as you wanted.
Not that I'd expect you to change your vote (the other issues you mentioned are
more significant).

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

From: Randy Brukardt
Sent: Sunday, June 19, 2011  10:48 PM

The accessibility checks for subpools are intended to ensure that the pool
object and the access type belong to the same master. This prevents trouble with
finalization (since the contents of subpools can be finalized when the pool
object goes away, rather than when the collection of the access type goes away).

Accessibility checks generally are designed to be conservative. That means, for
instance, that (non-aliased) parameters are assumed to be local. This however
causes problems for the subpool pool accessibility check. Consider:

   procedure Do_It (A_Pool : in out Root_Storage_Pool_With_Subpools) is

      Local : Natural;

      type A_Controlled_Type is new Ada.Finalization.Controlled with...

      procedure Finalize (Object : in out A_Controlled_Type) is
      begin
          ... <<use Local here>> ...
      end Finalize;

      type A_Acc is access A_Controlled_Type
          with Storage_Pool => A_Pool; -- (A)
      AA : A_Acc := new A_Controlled_Type;
   begin
      ...
   end Do_It;

   Some_Pool : Some_Pool_Type_with_Subpools;

   Do_It (Some_Pool);

The accessibility check at (A) will pass, because A_Pool is treated as if it is
local for the purposes of accessibility (so we don't have to pass runtime
accessibility levels). But this means that A_Acc and Some_Pool are in different
masters. And the finalization of the allocated objects of type A_Acc might not
occur until Some_Pool is finalized -- which means the finalization is going to
access a non-existent local variable. Ouch!

I've thought of a number of ways to fix this. It should be noted that such a
pool as a parameter will never work for a pool that supports subpools. So a
straight legality rule would work -- except that it would be incompatible (since
pools of Root_Storage_Pool'Class have to be assumed to be able to have
subpools). (The incompatibility would be pretty unlikely, as this would be an
unusual thing to do.) A runtime check would work, but it is a bit weird,
especially for a case this unlikely.

Exactly how to word a rule seems interesting, too. It would be best to do
something to "fix" the accessibility levels for cases like this (parameters are
not *quite* the same level as local objects), but that seems very messy (I tried
it for aliased parameters, and I managed to introduce lots of bugs, so we
abandoned it).

Anyway, I'll put this into the subpool discussion AI for the meeting.

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

Questions? Ask the ACAA Technical Agent