Overview of Ada 2022
Jeff Cousins
Contents   Index   Search   Previous   Next 

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.
An example of using the Dispatching aspect to manage the globals accessed is found in 2.4, “Defining access to global data”.

Contents   Index   Search   Previous   Next 
© 2021, 2022 Jeff Cousins