Version 1.1 of acs/ac-00287.txt

Unformatted version of acs/ac-00287.txt version 1.1
Other versions for file acs/ac-00287.txt

!standard 13.11.4(6/3)          17-04-06 AC95-00287/00
!class confirmation 17-04-06
!status received no action 17-04-06
!status received 17-01-24
!subject Problem with subpools?
!summary
!appendix

From: Jean-Pierre Rosen
Sent: Tuesday, January 24, 2017  12:39 PM

I am trying to implement a very simple subpool (as an example) and I just don't
know how to do what I want. Wondering if I missed something, or if there is a
real problem...

Here it is: I have a simple (fixed size) allocator. I have a number of slots,
and every allocation allocates a full slot.

I could divide this pool into subpools easily: a subpool is just a number, and
every slot is marked with the subpool's number it belongs to.

So it seems that I should declare:
type My_Subpool is new Root_Subpool with
   record
      Subpool_Id : Some_Counter_Type;
   end record;

Now, how do I implement Create_Subpool? I can't dynamically allocate an object
of type My_Subpool, because subpool_handle has storage_size 0. It seems I must
preallocate subpools in an array (as in the RM example), but there is no reason
to do so here. And it seems strange that every subpool has to be predeclared,
with a fixed maximum number of subpools.

Moreover, Ada.Unchecked_Deallocate_Subpool seems to imply that subpools could be
dynamically allocated, but this is not possible due to storage_size 0.

Is there a good reason for this storage_size 0 (I didn't find any in the AARM)?
Or did I miss something?

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

From: Randy Brukardt
Sent: Tuesday, January 24, 2017   2:47 PM

> Now, how do I implement Create_Subpool? I can't dynamically allocate
> an object of type My_Subpool, because subpool_handle has storage_size
> 0.

That's intentional; this is just a handle with no semantics of its own.

> It seems I must preallocate subpools in an array (as in the RM
> example), but there is no reason to do so here. And it seems strange
> that every subpool has to be predeclared, with a fixed maximum number
> of subpools.

No.

> Moreover, Ada.Unchecked_Deallocate_Subpool seems to imply that
> subpools could be dynamically allocated, but this is not possible due
> to storage_size 0.
>
> Is there a good reason for this storage_size 0 (I didn't find any in
> the AARM)? Or did I miss something?

The expectation is that if you want to dynamically allocate the subpool
information, you'll want to control the storage pool into which that happens. So
you need a separate type declaration for the access type in YOUR subpool
package.

In your case:

    type My_Subpool_Handle is access all My_Subpool
        with Storage_Pool => ...;

You then allocate (and deallocate) using this type. As Jeff has reminded me, Ada
allows conversions between any access-to-tagged types where the conversions
between the tagged types would be allowed. So you can just directly convert
My_Subpool_Handle to Subpool_Handle (or vice-versa) when needed. [The reverse
conversion will involve a tag check, but that would only fail if someone gives
you some other pool's handle - which you couldn't do anything useful with
anyway.]

I suppose it is a bit clunky if you really don't care what pool gets used for
the allocations. But if your pool is intended to be reusable, you do care, since
someone could use the global Default_Storage_Pool pragma and force your
allocations to an unknown pool (or even no pool at all). So it would be best to
force the standard pool on your allocations with a pragma Default_Storage_Pool
(Standard); in your package near the type declaration of My_Subpool_Handle. My
point being that even if Subpool_Handle didn't have a Storage_Size of 0, you
still couldn't reliably use it to do allocations in truly reusable code. Moral:
declare your own access types (and hide them in the body) as much as possible.

Brad has implemented a number of subpools, he probably can give you more details
about how he does these things. (OTOH, he didn't know that these access types
were interconvertable until I pointed it out last week.)

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

From: Jean-Pierre Rosen
Sent: Tuesday, January 24, 2017  3:59 PM

Thanks Steve and Randy, it works that way (and I won't look too stupid to my
students).

However...

> You then allocate (and deallocate) using this type. As Jeff has
> reminded me, Ada allows conversions between any access-to-tagged types
> where the conversions between the tagged types would be allowed.

This means that an access type with storage_size 0 can point into the heap. And
also that you can try to uncheck_deallocate such a type at the cost of a
conversion to another type.

You can argue that if you do that, you are supposed to know what you are doing,
but I find it quite surprising (and that's why I didn't even try the conversion
trick in the first place - my fault, in Ada you should always try with a
compiler before assuming anything ;-) ).

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

From: Tucker Taft
Sent: Tuesday, January 24, 2017   4:22 PM

...
> This means that an access type with storage_size 0 can point into the
> heap. And also that you can try to uncheck_deallocate such a type at
> the cost of a conversion to another type.

Yes, very true.  The Storage_Size 0 means that there is no storage pool
associated with the access type.  But if it is a general access type (rather
than a pool-specific access type), you can convert to and from the type and
thereby have values of the type that do point into some other access type's
storage pool.

> You can argue that if you do that, you are supposed to know what you
> are doing, but I find it quite surprising (and that's why I didn't
> even try the conversion trick in the first place - my fault, in Ada
> you should always try with a compiler before assuming anything ;-) ).

General access types permit conversion, and so that means that a value of such a
type can point into various different pools, and care is certainly required if
you start doing unchecked deallocation.

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

From: Randy Brukardt
Sent: Tuesday, January 24, 2017  4:27 PM

> You can argue that if you do that, you are supposed to know what you
> are doing, but I find it quite surprising (and that's why I didn't
> even try the conversion trick in the first place - my fault, in Ada
> you should always try with a compiler before assuming anything ;-) ).

Since Ada 95 allows these sorts of conversions, it would be difficult to try to
take them away in some circumstances without causing generic contract problems
(which then morph into compatibility problems if the contract problems are
fixed). We decided not even to try for the Storage_Size = 0 case.

And you surely can't do this by accident, which is what good programming
language design is trying to prevent. It never pays to prevent people from doing
what they want, because they'll find some way to do anyway (I've seen lots of
Unchecked_Conversions to make conversions that the language doesn't allow). I
don't think that banning conversions to/from Storage_Size = 0 types would have
helped anything, it just would have made it more annoying.

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

From: Brad Moore
Sent: Tuesday, January 24, 2017  11:47 PM

> Brad has implemented a number of subpools, he probably can give you
> more details about how he does these things. (OTOH, he didn't know
> that these access types were interconvertable until I pointed it out
> last week.)

I think I was aware of the interconvertable-ness of access types, but I think
what happened in my case was that somewhere along the line I started running
into accessibility check failures, without understanding why. (Not an entirely
uncommon problem I suspect.) This led me to finding a work around involving the
use of 'Unchecked_Access. Once I started using that work around, it started
appearing in places where I probably didn't need it.

A case in point, in my storage pools I was instantiating
Address_To_Access_Conversions at a nested level, and trying to convert from the
access type produced by that instantiation, to an access type supplied by the
user as a generic formal.

Randy pointed out that I get resolve the accessibility failure by moving the
instantiation to library level, which I could do in my current version of the
code which was a generic package, but not in earlier versions, where the code
was written as a generic function.

It would have been helpful if the compiler could have generated a more detailed
explanation of why accessibility checks are failing, but I imagine its hard
enough to understand how to get the compiler to implement the checks in the
first place, let alone explain to the user why an accessibility check is
failing.

For the Address_To_Access_Conversions case, it might be helpful to have the
compiler generate a warning if one tries to instantiate that package at a nested
level.

I understand from Randy that there is a bug in the design of this package, due
to the access type being local to the package specification. Presuming that
there isn't enough motivation to fix this bug, a warning might at least
encourage users to avoid the pitfall that I ran into, as a result of this bug.

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


Questions? Ask the ACAA Technical Agent