Version 1.3 of ais/ai-00058.txt
!standard E.2.1 (08) 99-03-22 AI95-00058/07
!class ramification 95-06-25
!status work item 95-06-25
!status received 95-06-25
!priority High
!difficulty Hard
!subject Accessibility Rules for Shared_Passive Packages
!summary
Consider a library unit P1, and a shared passive library unit P2. If
the declaration of P2 depends semantically on the declaration of P1,
then the accessibility level of P2 is known (at all places) not to be
statically deeper than that of P1. Now consider a place within the
declarative region of P2. If this place is within the scope of a with
clause that mentions P1, then the accessibility level of P2 is known (at
that place) not to be statically deeper than that of P1. Otherwise, the
accessibility level of P1 is considered to be statically deeper than
that of P2.
Compile-time accessibility rules are enforced accordingly.
The same definition applies to "deeper than" as for "statically deeper
than" above.
For a run-time Accessibility_Check that involves accessibility levels
defined in terms of a shared passive library unit and one that is not
shared passive, then the check is done as usual -- Program_Error is
raised if the check fails.
However, if the accessibility levels to be checked (at run time) are
defined in terms of accessibility of two shared passive library units,
then an implementation need not perform the check. If the
implementation ensures that all shared passive library units have the
same lifetime as the program as a whole, and are universally
addressable, then omitting such checks cannot cause dangling pointers.
On the other hand, if the implementation allows shared passive
partitions to come and go during execution of the program as a whole, or
if some shared passive partitions are not addressable from others, then
the undetected failure of such a check can cause dangling pointers; in
that case, the program's execution is defined to be erroneous.
!question
E.2.1(8-8.b) says:
8 {accessibility [from shared passive library units]} {notwithstanding}
Notwithstanding the definition of accessibility given in 3.10.2, the
declaration of a library unit P1 is not accessible from within the
declarative region of a shared passive library unit P2, unless the shared
passive library unit P2 depends semantically on P1.
8.a Discussion: We considered a more complex rule, but dropped it.
This is the simplest rule that recognizes that a shared passive
package may outlive some other library package, unless it depends
semantically on that package. In a nondistributed program, all
library packages are presumed to have the same lifetime.
8.b Implementations may define additional pragmas that force two
library packages to be in the same partition, or to have the same
lifetime, which would allow this rule to be relaxed in the presence
of such pragmas.
But 3.10.2 uses the notions of "accessibility levels" that are "the same
as" or "deeper than" other accessibility levels. So what does E.2.1(8)
mean by "accessible from"?
!response
Within a single partition, it is possible to have an access value that
designates a library level object in any library package. This model
assumes that library-level data is addressable from pretty much
anywhere, and that all library units have the same lifetime.
However, in a multi-partition program, these properties do not hold.
Data in an active partition is not addressable from any other partition.
Data in a shared passive partition may or may not be addressable,
depending on the with_clauses. Furthermore, partitions can come and go
at arbitrary times. The goal of E.2.1(8) is to prevent dangling pointer
problems that could be caused by pointers from shared passive partitions
to other (active or shared passive) partitions. By "dangling pointer",
we really mean two situations here: (1) dereferencing a pointer to some
data in a different partition that is not addressable from this
partition; (2) dereferencing a pointer to some data in a different
partition, when that partition no longer exists.
Note that Annex E assumes the following properties:
- If a normal package with's a shared passive package, then the
shared passive partition containing the shared passive package
lives at least as long as the active partition containing the
normal package.
- If a shared passive package with's another shared passive package,
then the shared passive partition containing the second package
lives at least as long as the shared passive partition containing
the first package.
In other words, a with_clause of A upon (shared passive) B constitutes a
statement that the partition containing A will (at all times) be able to
address the partition containing B.
Example 1:
package P1 is
X: aliased Integer;
end P1;
package P2 is
pragma Shared_Passive(P2);
type A is access all Integer;
Y: A;
end P2;
with P1, P2;
procedure Main is
begin
P2.Y := P1.X'access; --
end Main;
The intent of E.2.1(8) is that the above assignment be illegal, and the
above summary achieves that. P1 is part of the active partition. If
the above were legal, we would have a pointer from the shared passive
partition to the active partition. A different active partition could
attempt to dereference P2.Y, when the variable P1.X is not even
addressable by that partition. The active partition containing P1.X
might not even exist anymore.
Note that the above summary implies that two library units can both be
deeper than each other:
Example 2:
package P1 is
pragma Shared_Passive(P1);
type A is access all Integer;
Y: A;
X: aliased Integer;
end P1;
package P2 is
pragma Shared_Passive(P2);
type A is access all Integer;
Y: A;
X: aliased Integer;
end P2;
with P1, P2;
procedure Main is
begin
P2.Y := P1.X'access; --
P1.Y := P2.X'access; --
end Main;
P1 and P2 are both statically deeper than each other. Therefore, both
assignments are illegal. This prevents problems caused by one shared
passive partition living longer than the other. It also prevents
problems of pointers across address spaces -- if another active
partition had visibility upon P1, it could follow the invalid pointer
into P2, which is not addressable.
If P2 were to say "with P1;" then we would know that P1 lives at least
as long as P2, so the "P2.Y := P1.X'Access;" assignment would be legal.
Note that the wording of the summary makes it clear that the with_clause
has to be on the declaration of P2, not the body. (It corrects the
wording of E.2.1(8), which says "unless the shared passive library unit
P2 depends semantically on P1", which makes no sense, since semantic
dependence is between pairs of library_items, not library units.)
The compile-time accessibility rules must be checked with this
potentially-circular notion of deepness in mind. However,
implementation of the corresponding run-time checks would be a serious
implementation burden, and would be inefficient. Consider:
Example 3:
package P1 is
pragma Shared_Passive(P1);
X: aliased Integer;
end P1;
package P2 is
pragma Shared_Passive(P2);
type A is access all Integer;
Y: A;
end P2;
procedure P3(Z: access Integer);
with P2;
procedure P3(Z: access Integer) is
begin
P2.Y := P2.A(Z); --
P2.Y := Z.all'access; --
end P3;
with P1, P3;
procedure Main is
begin
P3(Z => P1.X'access);
end Main;
In this case, it is not known at compile time of P3, whether Z
designates an object in P1. Furthermore, given the potential
circularities in deepness, it is not easy to check this at run time.
Therefore, we define the above situation to be erroneous on any
implementation where P3 cannot address P1.
The accessibility rules in question are those on 'Access (where the
result type is a general access type, or an access-to-subprogram type),
type_conversion (where the target type is a general access type, or an
access-to-subprogram type), and type extension. Note that the
accessibility rule in 9.5.4(6), which has to do with requeues, is
irrelevant to this discussion.
However, there is one case in which the run-time error can be easily and
efficiently detected: An implementation can prevent the creation of
pointers from shared passive partitions to active partitions by
assigning an accessibility level of -1 to shared passive library units,
and doing the run-time checks accordingly (presuming normal library
units are at level 0). This is likely to be a common case. We
therefore require this particular case to be detected. Note that this
technique is not general, since it does not prevent the creation of
pointers from one shared passive partition to another shared passive
partition.
If we were to remove the "pragma Shared_Passive(P1);", then P1 would be
part of the active partition, and the lines marked "-- Possibly
erroneous." would instead raise Program_Error.
Example 4 illustrates various consequences of the rules given in this
AI:
package P1 is
pragma Shared_Passive(P1);
type A is access all Integer;
Y: A;
X: aliased Integer;
type T1 is tagged null record;
protected type Protected_Type is ... end; --
Protected_Object: Protected_Type;
end P1;
package P2 is
pragma Elaborate_Body(P2); --
pragma Shared_Passive(P2);
type A is access all Integer;
Y: A;
X: aliased Integer;
end P2;
with P1;
package body P2 is
type T2 is new P1.T1 with null record; --
--
--
function F return P1.Protected_Type is
begin
return P1.Protected_Object; --
end F;
procedure Proc is
begin
Y := P1.X'access; --
end Proc;
end P2;
generic
package G is
pragma Shared_Passive(G);
procedure Proc;
end G;
with P1;
package body G is
function F return P1.Protected_Type is
begin
return P1.Protected_Object; --
end F;
procedure Proc is
begin
... F ...
end Proc;
end G;
package P3 is
pragma Elaborate_Body(P3); --
pragma Shared_Passive(P3);
type A is access all Integer;
Y: A;
X: aliased Integer;
end P3;
with G;
package body P3 is
package G_Instance is new G;
begin
G_Instance.Proc; --
end P3;
Example 5 illustrates why the definition in the summary depends on the
place:
package P1 is
X: aliased Integer;
end P1;
package P2 is
pragma Elaborate_Body; --
pragma Shared_Passive(P2);
type A is access all Integer;
Y: A;
end P2;
with P1, P2;
procedure Main is
begin
P2.Y := P1.X'access; --
end Main;
with P1;
package body P2 is
begin
Y := P1.X'access; --
end P2;
In the body of P2, we are within the scope of "with P1;", so the
accessibility level of P2 is known not to be statically deeper than that
of P1. Therefore, the statement marked "-- OK." is legal. However, in
Main, we don't know about the "with P1;" on the body of P2, so the same
statement at that place is illegal.
!appendix
!section E.2.1(08)
!subject Vestigial reference to old accessibility rules
!reference RM9X-E.2.1(8);5.95
!from Norman Cohen
!reference as: 94-5035.c Norman H. Cohen 94-12-19>>
!discussion
This paragraph still talks of "accessibility from" a declarative region
rather than accessibility levels. Oh well. A clarification in the AARM
would be in order.
****************************************************************
!section E.2.1(08)
!subject Vestigial reference to old accessibility rules
!reference RM9X-E.2.1(8);5.95
!reference 94-5035.c Norman H. Cohen 94-12-19
!from Bob Duff
!reference as: 94-5037.c Bob Duff 94-12-19>>
!discussion
> This paragraph still talks of "accessibility from" a declarative region
> rather than accessibility levels. Oh well. A clarification in the AARM
> would be in order.
Caught red handed!
I knew about this bug, and decided it wasn't worth fixing.
It's not clear how to word it using "levels", since these
things don't have a particular order -- the core language doesn't
have any similar notion, where things are inaccessible from
each other.
However, I think the intent is clear. We'll see if the compiler writers
agree...
****************************************************************
!section E.2.1(08)
!subject request for clarification
!reference RM95-E.2.1(8)
!from Tom Burger (tom@ixi.saic.com) 95-06-12
!keywords accessible
!reference as: 95-5154.a Tom Burger 95-6-13>>
!discussion
While developing ACVC tests for the Distribution Annex I found
the meaning of this paragraph to be quite elusive. Further
explanation or an example would be very helpful. Thanks.
****************************************************************
!section E.2.1(08)
!subject request for clarification
!reference RM95-E.2.1(8)
!from Tom Burger (tom@ixi.saic.com) 95-06-12
!keywords accessible
!reference 95-5154.a Tom Burger 95-6-13
!reference as: 95-5156.a Robert A Duff 95-6-14>>
!discussion
> While developing ACVC tests for the Distribution Annex I found
> the meaning of this paragraph to be quite elusive. Further
> explanation or an example would be very helpful. Thanks.
package P1 is
X: aliased Integer;
end P1;
package P2 is
type A is access Integer;
Y: A;
end P2;
Is "Y := X'Access;" legal? According to 3.10.2, this is legal.
If we make P2 Shared_Passive, it's illegal, by E.2.1(8), unless
we add "with P1;" to P2.
Normally, all library packages are in the same address space, and live
"forever", so you can freely create pointers from one to the other.
But if a P2 is shared passive, it can live longer than P1, which might
be part of a partition that executes and then disappears. So we don't
allow pointers from P2 to P1. OTOH, if P2 says "with P1;", then P1 will
live at least as long as P2, so E.2.1(8) doesn't forbid the 'Access in
that case.
The "Y := X'Access;" statement is in some active partition, I'm
assuming, and it says "with" of P1 and P2.
The "accesible from" wording is somewhat of a cheat, I admit, since
3.10.2 is written in terms of "levels" and "deeper than". In E.2.1,
it's not a question of being deeper, it's just that this thing can't
point to that thing.
- Bob
****************************************************************
!section E.2.1(08)
!section E.2.1(8)
!subject request for clarification
!reference RM95-E.2.1(8)
!reference AARM-E.2.1(8a)
!reference 95-5154.a Tom Burger 95-6-13
!from Anthony Gargaro 95-06-14
!keywords accessible, shared passive packages
!reference as: 95-5157.a Anthony Gargaro 95-6-14>>
!discussion
> While developing ACVC tests for the Distribution Annex I found
> the meaning of this paragraph to be quite elusive. Further
> explanation or an example would be very helpful. Thanks.
The purpose of this paragraph is to ensure that values accessible through a
shared passive package always designate valid objects. For example, unless
this rule is present, consider the consequences of compiling and partitioning
the following library units:
package SP is
pragma Shared_Passive;
type Ref_F is access function return Integer;
F : Ref_F;
end SP;
package Abnormal is
pragma Pure;
function F return Integer;
end Abnormal;
package body Abnormal is
function F return Integer is
begin
return 100;
end F;
end Abnormal;
with SP, Abnormal, Text_IO;
procedure Ptn_1 is
begin
SP.F := Abnormal.F'Access;
Text_IO.Put_Line("Value is" & Integer'Image(SP.F.all));
end Ptn_1;
with SP, Text_IO;
procedure Ptn_2 is
begin
Text_IO.Put_Line("Value is" & Integer'Image(SP.F.all));
end Ptn_2;
There is no guarantee that Ptn_1 and Ptn_2 will be assigned to the same
partition; thus, the assignment to SP.F by Ptn_1 may result in Ptn_2
dereferencing an invalid access value in another partition. By requiring that
SP semantically depends upon Abnormal protects against such an invalid
reference.
****************************************************************
!section E.2.1(00)
!subject General access types should be disallowed in Shared_Passive
!reference RM95-E.2.1
!from Laurent Guerby 96-08-23
!keywords distribution, Shared_Passive, general access type
!reference 96-5645.a Laurent Guerby 96-8-23>>
!discussion
E.2.1 Shared Passive Library Units
1 [A shared passive library unit is used for managing global data shared
between active partitions. The restrictions on shared passive library units
prevent the data or tasks of one active partition from being accessible to
another active partition through references implicit in objects declared in
the shared passive library unit.]
>> Sources showing the issue.
-- On some storage node.
package SP is
pragma Shared_Passive;
type A is access all Integer;
-- General access type.
P : A;
end SP;
-- On some processing node
with SP;
package Local_PN is
I : aliased Integer := 2;
end Local_PN;
with SP, Local_PN;
procedure Main_PN is
begin
SP.P := Local_PN.I'access;
-- SP.P points back to PN address space.
end Main_PN;
--
Laurent Guerby <guerby@gnat.com>, Team Ada.
"Use the Source, Luke. The Source will be with you, always (GPL)."
****************************************************************
!section E.2.1(00)
!subject General access types should be disallowed in Shared_Passive
!reference RM95-E.2.1
!keywords distribution, Shared_Passive, general access type
!reference as: 96-5645.a Laurent Guerby 96-8-23
!from Anthony Gargaro 96-8-24
!reference 96-5648.a Anthony Gargaro 96-8-24>>
!discussion
>> Sources showing the issue.
> ...
>with SP, Local_PN;
>procedure Main_PN is
>begin
> SP.P := Local_PN.I'access;
> -- SP.P points back to PN address space.
>end Main_PN;
It is my understanding that this example is illegal since
SP does not depend upon Local_PN as required by E.2.1(8).
****************************************************************
!section E.2.1(00)
!subject General access types should be disallowed in Shared_Passive
!reference RM95-E.2.1
!keywords distribution, Shared_Passive, general access type
!reference 96-5645.a Laurent Guerby 96-8-23
!from Anthony Gargaro 96-8-24
!reference 96-5648.a Anthony Gargaro 96-8-24
!from Bob Duff
!reference 96-5653.a Robert A Duff 96-8-27>>
!discussion
> >> Sources showing the issue.
>
> > ...
>
> >with SP, Local_PN;
> >procedure Main_PN is
> >begin
>
> > SP.P := Local_PN.I'access;
> > -- SP.P points back to PN address space.
>
> >end Main_PN;
>
> It is my understanding that this example is illegal since
> SP does not depend upon Local_PN as required by E.2.1(8).
That is certainly the intent. This is the subject of AI-58, which has
never been considered by the ARG, nor have I written a draft version of
the AI. Clearly, the above should be illegal. However, it is not
entirely clear to me what the rule is in the case of dynamic
accessibility checks -- that is, imagine passing Local_PN.I'access as an
access parameter.
- Bob
****************************************************************
!section E.2.1(08)
!subject AI-58: Editorial
!reference AI95-00058/04
!from Offer Pazy 96-11-22
!reference 96-5767.a Offer Pazy 96-11-22>>
!discussion
I believe that there is a small error in the discussion of Example 2:
> ...
> with P1, P2;
> procedure Main is
> begin
> P2.Y := P1.X'Access; --Illegal.
> P1.Y := P2.X'Access; --Illegal.
> end Main;
>
> . . .
>
> If P2 were to say "with P1;" then we would know that
> P2 lives at least as long as P1,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> so the "P2.Y := P1.X'Access;" assignment would be legal.
I think that this is backwards: If P2 withes P1, then we know that P1 lives
as least as long as P2, not the other way arround. The assignment is still
(or "therefore" :-) legal.
Offer Pazy
48 Be'eri St.
Tel-Aviv 64233
Israel
972-3-695-1923
pazy@world.std.com
****************************************************************
!section E.2.1(08)
!subject AI-58: Drop the support for hierarchical memory configurations
!reference RM95-E.2.1(8)
!reference AI95-00058/04
!from Offer Pazy 96-11-22
!reference 96-5768.a Offer Pazy 96-11-22>>
!discussion
I would like to start by arguing that resorting to erroneous execution in
this case is very bad news. The features discussed and the interactions
among them are not considered unsafe, they cannot be easily localized and
the erroneous usage is likely to result from actions of programmers on the
one hand and system builders on the other. Also, note that it is very likely
that the affected applications are (very) large programs. It is not clear to
me what guidelines one could give to his group to avoid such cases without
significantly limiting the usefulness of Annex E. Errors resulting from this
can be extremely subtle and very hard to find (let alone predict in advance)
even for a more experienced user. Most other cases where we have resorted to
erroneousness are either obscure or use unsafe features and hence are
usually marked by a pragma or a certain with clause. This is not the case
here, where the constructs are "mainstream: and their usage may appear in
many places in the code's body.
So this is my main problem and I would like the ARG to consider this AI with
this in mind, this is a very serious erroneousness case.
Based on this general objective, I would like to propose another approach to
address the real problem that is expressed in the AI, and that is to abandon
the (very ambitious) goal of supporting in the standard the idea of
hierarchical memory configurations (HMCs) (those that allow passive
partitions to come and go). From the outset, we knew that the support of
this is at best partial; we leave all issues of explicit partition abort,
reload, and restart outside the ARM, features that must be provided in some
form by the implementation to support HMCs. Furthermore, the partitioning
and configuration steps are treated by the ARM as conceptual only, we give
very few hints on what they should do. This was done intentionally since we
understood that we cannot achieve more standardization here.
Essentially, what has happened at an early stage of the mapping, was that we
saw this "nice feature" and thought that we could get this interesting model
in the annex, almost free. Later revisions, and this AI proved us wrong. In
order to really support HMCs, much more definition work in needed in the
language to do it right, and it's not easy. Furthermore, while some may
argue, I do believe that there are still many applications for the flat
memory model without supporting HMCs; it's nice to have, but not critical.
If the objective of supporting HMCs causes us to make straight-forward
distributed programs erroneous in a serious manner, then I think we are
making the wrong decision.
So basically, my suggestion is to give up on the standard support of HMCs.
We should leave this entire model as impl-defined (as is most of it now
anyway) and fix whatever is necessary in the accessibility rules for the
flat model (the AI does it already as is discussed under the third example).
The flat model will be the default one and the only model required by the
annex. Implementations will then be free to support HMCs fully and to add
whatever pragmas or rules to address this specific problem.
I think that the majority of users will gain by this!!!
Offer Pazy
48 Be'eri St.
Tel-Aviv 64233
Israel
972-3-695-1923
pazy@world.std.com
****************************************************************
From: Anthony Gargaro
Sent: Thursday, March 25, 1999 6:03 AM
Two minor comments on this version. [Version 07, Ed.]
The wording shared passive partitions should be changed to
simply passive partitions.
In example 5, a pragma Shared_Passive should apply to P1,
since P2 semantically depends upon P1.
Anthony.
****************************************************************
Questions? Ask the ACAA Technical Agent