!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. ***************************************************************