Overview of Ada 2022
2.3 Defining Nonblocking
Nonblocking subprograms (AI12-0064-2)
adds the aspect Nonblocking to Ada (see RM
9.5).
This allows specifying the blocking status of a subprogram. If a subprogram
is declared to be non-blocking, the Ada compiler will attempt to verify
that it does not execute any potentially blocking operations. Potentially
blocking operations include
select,
accept,
entry
call,
delay and
abort statements, creating or activating
a task, or calling something else which in turn executes one of these.
The language also includes some deadlocks in the
definition of “potentially blocking”, mostly to simplify
the description of the language (deadlocks have nothing to do with blocking).
These cases cannot be detected at compile time, but the run-time check
of pragma
Detect_Blocking can be used if it
is desired to detect these problems.
Note that the Nonblocking
aspect may be specified for generic formal parameters.
Rather than specify the Nonblocking
aspect for many individual subprograms, it can be specified for a package,
protected type, task or generic, in which case it
sets the default for everything within. Indeed for a protected type,
it may not be specified for individual operations within.
The Nonblocking aspect
is fixed as False for an entry, and
as True for a predefined operator of an elementary
type, as one might expect.
Contracts for container operations (AI12-0112)
then uses the
Nonblocking aspect to specify
the non-blocking status for the predefined
Containers.
Specifying Nonblocking for Language-Defined Units
(AI12-0241)
then uses the
Nonblocking aspect to specify
the non-blocking status for the remainder of Ada's own units (that is,
child units of packages
Ada,
System
and
Interfaces).
Nonblocking for Unchecked_Deallocation is wrong
(AI12-0319)
tidies up some of the details of the
Nonblocking
aspect. It may seem strange that a type can have blocking properties,
not just subprograms having them, but an object coming into or out of
existence is not always a passive affair. If it is of a controlled type,
then it may have
Initialize,
Adjust
and
Finalize procedures; there might be default
initialisation of record components, which could call a function; and
if on the heap there will be
Allocate,
Deallocate,
and
Storage_Size subprograms. For a type to
be non-blocking, whichever of these are applicable need to be non-blocking
too. (Default initialisation of scalars is not of concern as the
Default_Value
aspect may only have a static value.)
The standard storage pool(s) are defined to be non-blocking.
For a user-defined storage pool to be non-blocking, its Allocate,
Deallocate, and Storage_Size
subprograms must also be non-blocking. The Unchecked_Deallocation
generic invokes a Finalize procedure, so for
an instance of it to be non-blocking it must be instantiated with a non-blocking
type.
Fixes for Nonblocking (AI12-0374-2)
clarifies what happens for generic instantiations. At the point of instantiation,
the
Nonblocking aspects of the actual generic
parameters are
and-ed with the
Nonblocking
aspects of the operations within the generic. Thus a non-blocking generic
can be instantiated with blocking actuals, in which case the instance
will allow blocking. If the instance is required to be non-blocking,
then the specific instance can be declared as such.
Also, the Nonblocking
aspect may be specified on subtypes, not just types, so that predicates
on some subtypes of a given type may call a blocking operation and predicates
on some other subtypes of the type may not.
The Nonblocking aspect
should also account for preconditions, postconditions, predicates and
type invariants applicable to the call of the subprogram.
AI12-0374-2
also removes the notion of a
Nonblocking attribute,
which had (temporarily) been added by
AI12-0064-2.
Fixups for Global annotations (AI12-0380)
(which, despite the name, is mostly applicable to both the
Nonblocking
aspect and the
Global annotations) provides
finer grain control regarding the use of generic formal parameters and
dispatching calls, in optional Annex H for High Integrity Systems.
It is common, especially in larger generic packages,
for some of the operations to depend on few or none of the generic parameters.
However, normally entities declared within a generic unit are presumed
to make use of all the generic formal parameters and the effective
Nonblocking
aspect and
Global annotation reflect this.
In order to better describe the use for formal parameters, this AI adds
aspect
Use_Formal (see RM
H.7.1)
followed by a list of which generic formal parameters are actually used
(enclosed by round brackets and comma-separated if more than one), the
reserved word
null for none, or the reserved word
all for
all of them (which is the default anyway).
For example, in the
containers packages, there are many operations (such as First,
Next and Length)
which simply depend on the state of a container object and do not depend
on any of the generic formal parameters. For such operations, we can
use
with Nonblocking, Global => null, Use_Formal => null,
and be assured that these operations will always
be nonblocking and never depend on any global objects regardless of the
actual parameters for an instance. This means that such operations can
be used inside of a parallel operation no matter what the conflict detection
state is (see
2.2).
Note that operations that create or copy elements
depend on the Element_Type formal parameter, which might allow blocking
or depend on global objects (via Initialize,
Adjust or Finalize).
Such operations cannot use Use_Formal => null
and must either omit the Use_Formal aspect
or use Use_Formal => Element_Type.
Some operations may be implemented by dispatching
calls. Normally dispatching calls are checked using the applicable Nonblocking and Global'Class aspects. Typically, the
Nonblocking aspect and Global
annotation for such calls are very conservative, as one does not want
to prevent complex implementations in extensions (even ones to be designed
in the future). In particular, most such calls allow blocking, as blocking
can be useful in some circumstances, and even the absense of blocking
in existing extensions does not prevent it from being added later. (Additionally,
the default is to allow blocking for compatibility reasons.)
Often a caller will know more about the routine actually
called in a dispatching call, as the type of the controlling object will
be known. This AI adds aspect
Dispatching
(see RM
H.7.1)
followed a list of dispatching calls (enclosed by round brackets and
comma-separated if more than one, and each followed by the name of an
object also enclosed by round brackets) that may potentially be called,
for which the caller of the original subprogram will account for the
blocking state and any globals accessed.
© 2021, 2022 Jeff Cousins