Version 1.1 of ai05s/ai05-0275-1.txt

Unformatted version of ai05s/ai05-0275-1.txt version 1.1
Other versions for file ai05s/ai05-0275-1.txt

!standard C.6(16)          11-12-27 AI05-0275-1/01
!class binding interpretation 11-11-09
!status Amendment 2012 11-12-27
!status ARG Approved 7-0-2 11-11-12
!status work item 11-11-09
!status received 11-10-01
!priority Low
!difficulty Easy
!qualifier Omission
!subject More issues with the definition of volatile
!summary
Atomic objects still must be used to ensure that actions are sequential before accessing volatile objects; nothing about the new version of C.6(16/3) is intended to imply that one can read shared volatile objects without some sort of synchronization.
!question
C.6(16/3) says:
All tasks of the program (on all processors) that read or update volatile variables see the same order of updates to the variables.
This wording is correct but seems misleading. Any concurrent accesses to shared variables (unless they are atomic) lead to erroneous execution, as clearly stated in 9.10(11). But C.6(16/3) because it seems to guarantee sequentially consistent behavior for concurrent accesses of non-atomic volatile variables, which is not the case.
Should this be clarified further? (Yes.)
!recommendation
(see summary.)
!wording
Modify C.6(16/3) and the following AARM note:
All tasks of the program (on all processors) that read or update volatile {@Redundant[and atomic]} variables see the same order of updates to the variables.{ A use of an atomic variable or other mechanism may be necessary to avoid erroneous execution and to ensure that access to volatile variables is sequential (see 9.10).}
AARM Implementation Note: To ensure this, on a multiprocessor, any read or update of {an atomic}[a volatile] object {may require}[should involve] the use of an appropriate memory barrier.
!discussion
C.6(16/3) is not about sequential actions at all, but rather about the view of updates that tasks have of the objects. The updates (especially of objects with multiple parts) may occur in any order, but all tasks will see the same order. In particular, it cannot be the case that task T1 sees the new version of part 1 of a volatile object V and the old version of part 2 of V, while task T2 sees the old version of part 1 of V and the new version of part 2 of V. Any other view of old and new versions of V is allowed (in the absence of other synchronization, of course).
Memory barriers are only needed between sequential actions involving shared variables. A typical way to ensure this is emit the memory barriers on accesses to atomic objects; this is what we suggest in the implementation note.
However, memory barriers may be required any time sequential actions are required for access to shared variables (of any category, including "normal" objects). In particular, protected actions and actions that signal another also may need barriers, or associated access to shared variables need the barriers. We do not try to determine any advice to give implementers in these other cases, because their implementation may vary wildly. For instance, some of these actions may require a call to an OS service. Others may be modeled as an atomic object (such as the spin lock of a protected action) and thus need no special effort.
!example
The example given in AI05-0117-1 is wrong, as it does not require sequential access to the objects. The following example is correct, and will ensure that task 2 does get the value 42.
Data : Integer; pragma Volatile (Data);
Flag : Boolean := False; pragma Atomic (Flag);
in task 1:
Data := 42; Flag := True;
in task 2:
loop exit when Flag; end loop; Copy := Data;
!corrigendum C.6(16)
Replace the paragraph:
For a volatile object all reads and updates of the object as a whole are performed directly to memory.
by:
All tasks of the program (on all processors) that read or update volatile and atomic variables see the same order of updates to the variables. A use of an atomic variable or other mechanism may be necessary to avoid erroneous execution and to ensure that access to volatile variables is sequential (see 9.10).
!ACATS Test
No additional ACATS test is required.
!ASIS
No ASIS effect.
!appendix

From: Geert Bosch
Sent: Tuesday, July 12, 2011  12:58 PM

As implementor, I would like to comment on AI-117.
The proposed new wording seeks to clarify, yet seems
slightly confused.

In particular, the example in the AI is incorrect:

!example
The following will ensure that task 2 does get the value 42.

 Data : Integer;
 pragma Volatile (Data);

 Flag : Boolean := False;
 pragma Volatile (Flag);

in task 1:
 Data := 42;
 Flag := True;

in task 2:
 loop
    exit when Flag;
 end loop;

 Copy := Data;

As written, the example will lead to erroneous execution, where
both task 1 and task 2 access shared variable Flag, at least one
of the accesses is a write, and the accesses are not sequential,
see RM 9.10(11).

The example requires flag to be Atomic, while Data can remain Volatile.

Note the new wording of C.6(16):
> All tasks of the program (on all processors) that read or update
> volatile variables see the same order of updates to the variables.

This wording is correct, but only because any concurrent accesses
to shared variables (volatile or not) lead to erroneous execution,
as clearly stated in 9.10(11). On its own C.6(16) may be misleading,
because it seems to guarantee sequentially consistent behavior for
concurrent accesses of non-atomic volatile variables, which is not
the case.

Additionally, because only atomic accesses are by definition
sequential (see C.6(16)), there is in general no need for memory
barriers between accesses to volatile variables. Barriers are only
needed between two atomic accesses and between any atomic access and
an access to a (potentially) shared variable by the same task.
A barrier may be needed between an atomic access and an access of a
shared non-volatile variable, to ensure that either the old or the 
new value will be read, see 9.10(15b). A typical implementation would
emit full barriers before and after atomic reads and writes, eliding
consecutive barriers without intervening shared variable accesses.

Please note that it would be impractical to allow non-sequential
read/write access to volatile variables, as without locking a partial
write would allow the reader to access the object while in an 
inconsistent state, if it cannot be updated atomically. Consider 
a 64-bit long float on a 32-bit system, or a record with discriminant.

So, to conclude, C.6(16) should have an additional note:

 Accesses to volatile variables must be sequential to avoid erroneous
 execution, see 9.10(11).

Additionally, Implementation Note C.6(16.a) should be replaced with:

 AARM Implementation Note: To ensure this, on a multiprocessor,
 any read or update of an atomic object should involve the use of
 appropriate memory barriers.

Here, I replaced "volatile" by "atomic" and did not say "a memory barrier"
but used the indefinite "memory barriers", as in the general case two
barriers may be needed per access.

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

From: Ed Schonberg
Sent: Friday, November 11, 2011  1:11 PM

Dear Alan,

Geert Bosch has forwarded the following to the ARG, hoping for 
comments. we are currently discussing his message, and all those 
present confess to an insufficient depth of understanding of the issue.
Could you comment on Geert's remarks? In particular, he claims that on
multiprocessors memory barriers are needed eventually for access to atomic
variables, but not for access to volatile.

This would seem to make a substantial performance difference on an important
Ada2012 domain.  What is your expert opinion on this?

The ARG eagerly awaits, and hopes to dispose of the issue one way or 
the other in the course of this meeting. No pressure, but we would love to
know by sunday!

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

From: Alan Burns
Sent: Saturday, November 12, 2011  6:14 AM

I can give my opinion, but please do not take this as definitive, I am not
an expert on low level multiprocessor hardware.

The requirement is to be able to write algorithms using shared variables that do not
get screwed up by the reordering of assignments to these shared variables. The
objective is to do this in a way that is efficient.

We attempted to do this by focusing on volatile - but I guess we forgot that to use
shared variables at all then the variable must be atomic (9.10(15)) - unless some
other signal is used which we don't want.

I am not sure why 9.10(15) only grants 'atomic' the property of sequential updates.
It could be argued that 'volatile' should also ensure sequential updates
- as volatile and atomic are defined to have an external effect: 1.1.3.

If 9.10(15) was changed to include volatile then the issues raised below would largely
do away. We want 'volatile' to enable shared variables to be used consistently.
But if they also have to be atomic in order to be used at all then that limited the
use of 'volatile'.

I've embedded some other comments below

...
> As written, the example will lead to erroneous execution, where both 
> task 1 and task 2 access shared variable Flag, at least one of the 
> accesses is a write, and the accesses are not sequential, see RM 
> 9.10(11).
>
> The example requires flag to be Atomic, while Data can remain Volatile.

Yes this would seem to be true.

So the 'style' here would be to use a simple flag that can be make atomic, but use
volatile for the arbitrary data that is being transferred.

...
> Additionally, Implementation Note C.6(16.a) should be replaced with:
>
>   AARM Implementation Note: To ensure this, on a multiprocessor,
>   any read or update of an atomic object should involve the use of
>   appropriate memory barriers.

If we stay with the current definition of volatile then the above suggestions appear
to be needed and valid. But I am not sure that the right approach is not to change
9.10(15) to include volatile

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

From: Randy Brukardt
Sent: Tuesday, December 27, 2011  11:22 PM

In Denver, we modified C.6(16/3) to read as follows:

All tasks of the program (on all processors) that read or update volatile variables
see the same order of updates to the variables. A use of an atomic variable or other
mechanism may be necessary to avoid erroneous execution and to ensure that access to
volatile variables is sequential (see 9.10).

I find that there is a clash in this wording. The first sentence is talking about
"volatile variables" to mean "volatile and atomic variables" (since "volatile" includes
"atomic"). The second sentence is talking about "volatile variables" to mean "volatile
but not atomic variables" (since nothing special is needed for atomic variables).

Probably the easiest way to fix this clash is to add "and atomic" to the first sentence
(giving "volatile @Redundant[and atomic] variables"). It would be better to somehow
exclude atomic from the second sentence, but everything I tried is really clunky, and
since C.6(17) talks about sequential actions of atomic objects, we're probably OK here.

Any better ideas are welcome.

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

Questions? Ask the ACAA Technical Agent