C.6 Shared Variable Control
[This subclause defines representation aspects that control the use of
The type of aspect Atomic is Boolean.
Aspect Description for Atomic: Declare
that a type, object, or component is atomic.
The type of aspect Independent is Boolean.
Aspect Description for Independent:
Declare that a type, object, or component is independently addressable.
The type of aspect Volatile is Boolean.
Aspect Description for Volatile: Declare
that a type, object, or component is volatile.
The type of aspect Full_Access_Only is Boolean.
for Full_Access_Only: Declare
that a volatile type, object, or component is full access.
The type of aspect Atomic_Components is Boolean.
Aspect Description for Atomic_Components:
Declare that the components of an array type or object are atomic.
The type of aspect Volatile_Components is Boolean.
Aspect Description for Volatile_Components:
Declare that the components of an array type or object are volatile.
The type of aspect Independent_Components is Boolean.
Aspect Description for Independent_Components:
Declare that the components of an array or record type, or an array
object, are independently addressable.
If any of these aspects are directly specified, the aspect_definition
shall be a static expression. If not specified for
(including by inheritance), the Atomic,
Atomic_Components, and Full_Access_Only each
aspects are is
False. If any of these aspects are specified True
for a type, then the corresponding aspect is True for all objects of
the type. If the Atomic aspect is specified True, then the aspects Volatile,
Independent, and Volatile_Component (if defined) are True; if the Atomic_Components
aspect is specified True, then the aspects Volatile, Volatile_Components,
and Independent_Components are True. If the Volatile aspect is specified
True, then the Volatile_Components aspect (if defined) is True, and vice
versa. When not determined by one of the other aspects, or for an object
by its type, the Volatile, Volatile_Components, Independent, and Independent_Components
aspects are False.
Aspects Volatile and Volatile_Components (when
defined) are equivalent. We provide the Volatile_Components aspect only
to give symmetry with Atomic_Components and Independent_Components aspects.
type is one for which the aspect
Atomic is True. An atomic
object (including a component) is one
for which the aspect Atomic is True, or a component of an array for which
the aspect Atomic_Components is True for the associated type, or any
object of an atomic type, other than objects obtained by evaluating a
A slice of an atomic array object is not itself atomic. That's necessary
as executing a read or write of a dynamic number of components in a single
instruction is not possible on many targets.
type is one for which the aspect
Volatile is True. A volatile
object (including a component) is
one for which the aspect Volatile is True, or a component of an array
for which the aspect Volatile_Components is True for the associated type,
or any object of a volatile type. In addition, every atomic type or object
is also defined to be volatile. Finally, if an object is volatile, then
so are all of its subcomponents [(the same does not apply to atomic)].
When True, the aspects Independent and Independent_Components specify
as independently addressable
the named object or component(s), or
in the case of a type, all objects or components of that type. All atomic
objects and aliased objects
to be specified as independently addressable.
Ramification: If the compiler cannot
guarantee that an object (including a component) for which aspect Independent
or aspect Independent_Components is True is independently addressable
from any other nonoverlapping object, then the aspect specification must
Similarly, an atomic object (including atomic
components) is always independently addressable from any other nonoverlapping
object. Any representation item which would prevent this from being true
should be rejected, notwithstanding what this Standard says elsewhere
(specifically, in the Recommended Level of Support).
The Full_Access_Only aspect shall not be specified
unless the associated type or object is volatile [(or atomic)]. A full
access type is any atomic type, or a volatile type for which the
aspect Full_Access_Only is True. A full access
object (including a component) is any atomic object, or a volatile object
for which the aspect Full_Access_Only is True for the object[ or its
type]. A Full_Access_Only aspect is illegal if any
subcomponent of the object or type is a full access object or is of a
generic formal type.
This last rule breaks privacy, but that is considered
OK for representation clauses when there is no clear alternative. Note
that atomic objects may be nested, so long as the outer atomic object
does not have the Full_Access_Only aspect True.
We disallow subcomponents of a generic formal type
in a Full_Access_Only object or type as the actual to a formal type can
be a full access type. We could have had a less restrictive rule, but
such a use is unlikely as full access only objects are intended to be
used to access memory-mapped devices with access restrictions, and those
will need a concrete mapping not possible for generic formal types.
It is illegal to specify either of the aspects Atomic
or Atomic_Components to have the value True for an object or type if
the implementation cannot support the indivisible and
reads and updates required by the aspect (see below).
It is illegal to specify the Size attribute of an atomic object, the
Component_Size attribute for an array type with atomic components, or
the layout attributes of an atomic component, in a way that prevents
the implementation from performing the required indivisible and
reads and updates.
If an atomic object is passed as a parameter, then the formal parameter
shall either have an atomic type or allow pass by copy. If an atomic
object is used as an actual for a generic formal object of mode in
, then the type of the generic formal object shall be atomic.
If the prefix
of an attribute_reference
for an Access attribute denotes an atomic object [(including a component)],
then the designated type of the resulting access type shall be atomic. If an atomic type is used as an actual for a generic formal derived type,
then the ancestor of the formal type shall be atomic.
rules apply to volatile objects and to full access
A formal parameter allows pass by copy if it is not aliased
it is of a type that allows pass by copy (that is, is not a by-reference
If a nonatomic subcomponent of a full access object
is passed as an actual parameter in a call then the formal parameter
shall allow pass by copy (and, at run time, the parameter shall be passed
by copy). A nonatomic subcomponent of a full access object shall not
be used as an actual for a generic formal of mode in out. The
for an Access attribute shall not denote a nonatomic subcomponent of
a full access object.
If the Atomic, Atomic_Components, Volatile, Volatile_Components,
Independent, Independent_Components, or Full_Access_Only aspect is True
for a generic formal type, then that aspect shall be True for the actual
type. If an atomic type is used as an actual for a generic formal derived
type, then the ancestor of the formal type shall be atomic. A corresponding
rule applies to volatile types and similarly to full access types a
volatile type is used as an actual for a generic formal array type, then
the element type of the formal type shall be volatile
If a type with volatile components is used as an
actual for a generic formal array type, then the components of the formal
type shall be volatile. Furthermore, if the actual type has atomic components
and the formal array type has aliased components, then the components
of the formal array type shall also be atomic. A corresponding rule applies
when the actual type has volatile full access components.
The limitations on formal array types are separate
for volatile and atomic because of the fact that only volatility is carried
down to all subcomponents of a volatile object, while atomicity is not.
The goal of both limitations is that we don't want 'Access for an access
type to produce a value that designates an object whose atomicity and
volatility don't agree with that of the designated type of the access
type. The above rules ensure that the generic “sees” the
relevant volatility and atomicity.
If an aspect Volatile, Volatile_Components, Atomic, or Atomic_Components
is directly specified to have the value True for a stand-alone constant
object, then the aspect Import shall also be specified as True for it.
Ramification: Hence, no initialization
expression is allowed for such a constant. Note that a constant that
is atomic or volatile because of its type is allowed.
Reason: Stand-alone constants that are
explicitly specified as Atomic or Volatile only make sense if they are
being manipulated outside the Ada program. From the Ada perspective the
object is read-only. Nevertheless, if imported and atomic or volatile,
the implementation should presume it might be altered externally. For
an imported stand-alone constant that is not atomic or volatile, the
implementation can assume that it will not be altered.
To be honest:
Volatile_Components and Atomic_Components actually are aspects of the
anonymous array type; this rule only applies when the aspect is specified
directly on the constant object and not when the (named) array type has
It is illegal to specify the aspect Independent or Independent_Components
as True for a component, object or type if the implementation cannot
provide the independent addressability required by the aspect (see 9.10
It is illegal to specify a representation aspect for a component, object
or type for which the aspect Independent or Independent_Components is
True, in a way that prevents the implementation from providing the independent
addressability required by the aspect.
For an atomic object (including an atomic component)
all reads and updates of the object as a whole are indivisible.
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 nonatomic volatile variables is
sequential (see 9.10
To ensure this, on a multiprocessor, any read or update of an atomic
object may require the use of an appropriate memory barrier.
it follows that (in non-erroneous
programs) accesses to variables, including those shared by multiple tasks,
are always sequential. This guarantees that no task will ever see partial
updates of any variable. For volatile variables (including atomic variables),
the above rule additionally specifies that all tasks see the same order
If for a shared variable X
, a read of X
after an update of X
, then the read will return the updated value
is volatile or atomic, but may or or may not return the updated
value if X
is nonvolatile. For nonvolatile accesses, a signaling
action is needed in order to share the updated value.
Because accesses to the same atomic variable by different tasks establish
a sequential order between the actions of those tasks, implementations
may be required to emit memory barriers around such updates or use atomic
instructions that imply such barriers.
Two actions are sequential (see
) if each is the read or update of the
same atomic object.
If a type is atomic or volatile
and it is not a by-copy type, then the type is defined to be a by-reference
type. If any subcomponent of a type is atomic or volatile, then the type
is defined to be a by-reference type.
If an actual parameter is atomic or volatile, and
the corresponding formal parameter is not, then the parameter is passed
Implementation Note: Note that in the
case where such a parameter is normally passed by reference, a copy of
the actual will have to be produced at the call-site, and a pointer to
the copy passed to the formal parameter. If the actual is atomic, any
copying has to use indivisible read on the way in, and indivisible write
on the way out.
Reason: It has to be known at compile
time whether an atomic or a volatile parameter is to be passed by copy
or by reference. For some types, it is unspecified whether parameters
are passed by copy or by reference. The above rules further specify the
parameter passing rules involving atomic and volatile types and objects.
All reads of or writes to any nonatomic subcomponent
of a full access object are performed by reading and/or writing all of
the nearest enclosing full access object.
} For example, if a 32-bit record object has four
nonatomic components, each occupying one byte, then an assignment to
one of those components might normally be implemented on some target
machines via some sort of store_byte instruction; if the record object
is an atomic full access object then instead a 32-bit read-modify-write
must be performed. That read-modify-write need not be atomic, although
the read and the write must each separately be atomic. Note that it doesn't
matter whether the store_byte instruction would have executed atomically.
This rule is needed in some cases for memory-mapped device registers.
atomic reads and writes associated with accesses to nonatomic components
of a full access object that is atomic are normal atomic operations —
all of the rules that apply to other atomic operations apply to these
as well. In particular, these atomic reads and writes are sequential
if they apply to the same object.
The external effect of a program (see 1.1.3
is defined to include each read and update of a volatile or atomic object.
The implementation shall not generate any memory reads or updates of
atomic or volatile objects other than those specified by the program. However, there may be target-dependent cases where reading or writing
a volatile but nonatomic object (typically a component) necessarily involves
reading and/or writing neighboring storage, and that neighboring storage
might overlap a volatile object.
Discussion: The presumption is that volatile
or atomic objects might reside in an “active” part of the
address space where each read has a potential side effect, and at the
very least might deliver a different value.
The rule above
and the definition of external effect are intended to prevent (at least)
the following incorrect optimizations, where V is a volatile variable:
X:= V; Y:=V; cannot be allowed to be translated
as Y:=V; X:=V;
Deleting redundant loads: X:= V; X:= V; shall
read the value of V from memory twice.
Deleting redundant stores: V:= X; V:= X; shall
write into V twice.
Extra stores: V:= X+Y; should not translate
to something like V:= X; V:= V+Y;
Extra loads: X:= V; Y:= X+Z; X:=X+B; should
not translate to something like Y:= V+Z; X:= V+B;
Reordering of loads from volatile variables:
X:= V1; Y:= V2; (whether or not V1 = V2) should not translate to Y:=
V2; X:= V1;
Reordering of stores to volatile variables:
V1:= X; V2:= X; should not translate to V2:=X; V1:= X;
The part about “target-dependent cases”
is intended to let compilers use a read-modify-write operation when a
volatile component has a size that cannot be directly read or written
with the available machine instructions. (For instance, writing a Boolean
component with size 1 in a volatile packed array of Boolean requires
a pre-read on most existing machines.)
This paragraph was
If the Pack aspect is True for a type any of whose
subcomponents are atomic, the implementation shall not pack the atomic
subcomponents more tightly than that for which it can support indivisible
reads and updates.
was deleted.Implementation Note:
Usually, specifying aspect Pack for such a type
will be illegal as the Recommended Level of Support cannot be achieved;
otherwise, a warning might be appropriate if no packing whatsoever can
Within the body of an instance of a generic unit
that has a formal type T that is not atomic and an actual type
that is atomic, if an object O of type T is declared and
explicitly specified as atomic, the implementation may introduce an additional
copy on passing O to a subprogram with a parameter of type T
that is normally passed by reference. A corresponding permission applies
to volatile parameter passing.
A load or store of a volatile object whose size is a multiple of System.Storage_Unit
and whose alignment is nonzero, should be implemented by accessing exactly
the bits of the object and no others, except in
the case of a volatile but nonatomic subcomponent of an atomic object
Implementation Advice: A load or store
of a volatile object whose size is a multiple of System.Storage_Unit
and whose alignment is nonzero, should be implemented by accessing exactly
the bits of the object and no others.
Since any object can be a volatile object, including packed array components
and bit-mapped record components, we require the above only when it is
reasonable to assume that the machine can avoid accessing bits outside
of the object. The exception is needed so this
advice doesn't conflict with other rules of this subclauase.
Ramification: This implies that the load
or store of a volatile object that meets the above requirement should
not be combined with that of any other object, nor should it access any
bits not belonging to any other object. This means that the suitability
of the implementation for memory-mapped I/O can be determined from its
documentation, as any cases where the implementation does not follow
Implementation Advice must be documented.
A load or store of an atomic object should, where possible, be implemented
by a single load or store instruction.
Implementation Advice: A load or store
of an atomic object should be implemented by a single load or store instruction.
9 An imported volatile or atomic constant
behaves as a constant (i.e. read-only) with respect to other parts of
the Ada program, but can still be modified by an “external source”.
Specifying the Pack aspect cannot override the
effect of specifying an Atomic or Atomic_Components aspect.
When mapping an Ada object to a memory-mapped hardware
register, the Ada object should be declared atomic to ensure that the
compiler will read and write exactly the bits of the register as specified
in the source code and no others.
is especially important for a write-only hardware register, as in such
a case a read-modify-write cycle will not work. The only time the language
guarantees such a cycle will not happen is when writing an entire atomic
object. If one wants to write individual components of a write-only hardware
register (assuming the hardware supports that), those also need to be
Incompatibilities With Ada 83
Pragma Atomic replaces Ada
83's pragma Shared. The name “Shared” was confusing, because
the pragma was not used to mark variables as shared.
Wording Changes from Ada 95
Added Implementation Advice to clarify the meaning of Atomic and Volatile
in machine terms. The documentation that this advice applies will make
the use of Ada implementations more predictable for low-level (such as
device register) programming.
Added wording to clarify that a slice of an object of an atomic type
is not atomic, just like a component of an atomic type is not (necessarily)
Incompatibilities With Ada 2005
Plugged a hole involving volatile
components of formal types when the formal type's component has a nonvolatile
type. This was done by making certain actual types illegal for formal
derived and formal array types; these types were allowed for Ada 95 and
Extensions to Ada 2005
Aspects Independent and Independent_Components are
new; they eliminate ambiguity about independent addressability.
Aspects Atomic, Atomic_Components, Volatile, and Volatile_Components
are new; pragma
Atomic, Atomic_Components, Volatile, and Volatile_Components are now
Wording Changes from Ada 2005
Revised the definition of volatile to eliminate overspecification and
simply focus on the root requirement (that all tasks see the same view
of volatile objects). This is not an inconsistency; "memory"
arguably includes on-chip caches so long as those are kept consistent.
Moreover, it is difficult to imagine a program that could tell the difference.
Added wording to take explicitly aliased parameters (see 6.1
into account when determining the legality of parameter passing of volatile
and atomic objects.
Inconsistencies With Ada 2012
Required that nonatomic components
of a atomic object use a read-modify-write cycle, as well as clarifying
that such a cycle is allowed for volatile objects that aren't exactly
the size of a machine scalar. That can introduce a read-modify-write
cycle where one was not used previously (for instance, if the components
are exactly byte-sized on most machines); it is thought that this will
more often fix bugs with access to hardware than it will cause them.
If this is an issue, declaring the components themselves as atomic will
restore the previous behavior.
Extensions to Ada 2012
These aspects now can be specified
for generic formal types.
Aspect Full_Access_Only is new; it can be used
to guarantee access to a complete device register for any operation even
when the register is mapped to a number of components.
Wording Changes from Ada 2012
Corrigendum: Clarified that aliased objects
are considered to be specified as independently addressable, and also
eliminated an unnecessary rule.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe