Rationale Update for Ada 2012

John Barnes
Contents   Index   References   Previous   Next 

Chapter 5: Tasking and real-time facilities

The major topic in this area is providing features for multiprocessors and increasing control for scheduling.
The following Ada Issues cover this area:
1
Independence and representation clauses for atomic objects
33*
Sets of CPUs when defining dispatching domains
48
Default behavior of tasks on a multiprocessor with a specified dispatching policy
51
The Priority aspect can be specified when Attach_ Handler is specified
52
Implicit objects are considered overlapping
55
All properties of a usage profile are defined by pragmas
73
Synchronous Barriers are not allowed with Ravenscar
81
Real-time aspects need to specify when they are evaluated
82
Definition of "dispatching domain"
90
Pre- and postconditions and requeues
98
Problematic examples for ATC
107
A prefixed view of a By_Protected_Procedure interface has convention protected
114
Overlapping objects designated by access parameters are not thread-safe
117
Restriction No_Tasks_Unassigned_To_CPU
129
Make protected objects more protecting
These changes can be grouped as follows.
First there are some clarifications and additions to dispatching domains which were added in 2012 (33, 48, 82). There are also some changes to the definition of the Ravenscar profile (55, 73, 117) and clarifications to some real-time aspects (51, 81). These are all in the Real-Time Systems annex (D).
The examples of the use of ATC (asynchronous transfer of control) need further explanation (98).
The question of being thread-safe in the face of overlapping objects has always been tricky and the text in the opening part of Annex A is modified (52, 114).
There are some improvements to the ability to control concurrent access in the core language (107, 129).
The required support for aspects such as Pack and their interactions with atomicity is rationalized (1). Note that this AI is a hangover from Ada 2005.
Finally, there are changes to the core language regarding pre- and postconditions and requeue (90).

As defined in Ada 2012 a dispatching domain consists of a set of processors whose CPU values are contiguous. However, this is unrealistic since CPUs are often grouped together in other ways. Accordingly, AI-33 adds a type CPU_Set and two functions to the package System.Multiprocessors.Dispatching_Domains thus
type CPU_Set is array (CPU range <>) of Boolean;
function Create(Set: CPU_Set) return Dispatching_Domain;
function Get_CPU_Set(Domain: Dispatching_Domain) return CPU_Set;
Moreover, the original functions Create and Get_Last_CPU are modified to be
function Create(First: CPU; Last: CPU_Range) return Dispatching_Domain;
function Get_Last_CPU(Domain: Dispatching_Domain) return CPU_Range;
The changes enable Last to be zero thereby allowing for null domains. Remember that the type CPU_Range has lower bound of zero whereas the subtype CPU has lower bound of one. If a domain is empty then Get_Last_CPU returns zero and Get_First_CPU returns one.
A minor editorial change is that many instances of Dispatching_Domain (which is a type name) are changed to dispatching domain (the concept) by AI-82. An important clarification concerns the behaviour in the absence of any use of CPU and dispatching domains. The summary of AI-48 says that in the absence of any setting of the CPU of a task and the creation of any dispatching domains, a partition that specifies a language-defined dispatching policy will allow all tasks to run on all processors.
With regard to Ravenscar, the whole essence of its intention is to enable the use of a very simple runtime system. Accordingly, the newly added synchronous barriers should not be allowed and so the additional restriction
No_Dependence => Ada.Synchronous_Barriers
is added to the definition of the Ravenscar profile by AI-73. Furthermore, in the case of multiprocessors, in order to permit analysis we need to ensure that all tasks (including the environment task) are assigned to a specific CPU and especially that no task is assigned zero which indicates Not_A_Specific_CPU. So a new restriction is introduced, namely
No_Tasks_Unassigned_To_CPU
and this is also added to the definition of the Ravenscar profile (AI-117). Moreover, Ravenscar requires that the CPUs are denoted statically so another restriction is introduced
No_Dynamic_CPU_Assignment
and this is also added to Ravenscar (AI-55).
There are some clarifications concerning aspects in the Real-Time annex. One is simply to say that the real-time aspects of a type are evaluated when an object of the task type is actually declared (AI-81). This applies to the aspects Priority and Interrupt_Priority and also to CPU. Remember that the aspects do not necessarily have to be static, in particular they could be discriminants of the type and different for different objects. Thus
task type Slave(N: CPU_Range)
   with CPU => N;
Tom: Slave(1);   Dick: Slave(2);   Harry: Slave(3);
It is easy to be confused regarding priorities and interrupt priorities. Typically, a protected procedure used as an interrupt handler would have aspects giving the priority and interrupt to be handled thus
procedure Handler
   with Attach_Handler => Some_Interrupt,
           Interrupt_Priority => Hot_Priority;
However, if the procedure Handler is in a protected type Monitor then the priority could be given on Monitor itself using the aspects Priority or Interrupt_Priority. If no interrupt priority or priority aspect is specified, the priority is implementation-defined but in the range of interrupt priority. (AI-51).
The mechanism for Asynchronous Transfer of Control (ATC) uses a form of select statement thus
select
   delay ... ;    -- triggering alternative
   ...
then abort
   Do_Something;    -- abortable part
end select;
This depends upon there being places in the abortable part that are abort completion points. The examples given in the RM in 9.7.4 rely upon this and some extra explanation is added (AI-98).
There has always been "boilerplate" in A(3) about reentrancy. The obvious example is that
task T1 is
begin
   Put(A_File, "Text");
end T1;
task T2 is
begin
   Put(A_File, "More Text");
end T2;
is not required to work (being unsafe use of a shared variable, namely A_File). But if we change T2 to
task T2 is
begin
   Put(B_File, "More Text");
end T2;
then it is required to work provided A_File and B_File are indeed truly different files. The wording in A(3) is improved to be more explicit with regard to parameter passing (AI-52, AI-114). It now becomes
"The implementation shall ensure that each language-defined subprogram is reentrant in the sense that concurrent calls on any language-defined subprogram perform as specified, so long as all objects that are denoted by parameters that could be passed by reference or designated by parameters of an access type are nonoverlapping."
An important difference between protected functions and protected procedures (and entries) is that protected functions can be accessed concurrently. The principle is that such functions should be used for interrogating state and not to change it. However, in the case of calling functions inside a container, they do often change state. Accordingly, to enable containers to be used by parallel tasks and to impose control of access by protected objects, it is necessary to be able to make protected functions behave like protected procedures and so prevent multiple access. This can be done by a new aspect Exclusive_Functions which can be given for a protected type or a single protected object. Thus we write
protected type PT
   with Exclusive_Functions;
   ...
and then all protected functions declared within PT have exclusive access. Note carefully that the aspect is not permitted on individual protected functions but on the protected type (or object) as a whole. (AI-129).
The usual convention for a prefixed view of a subprogram is Intrinsic which means that 'Access cannot be applied. However, an awkward situation has been discovered in the case of a subprogram with aspect Synchronization being By_Protected_Procedure. (Remember that the possible values for the aspect Synchronization are By_Entry, By_Protected_Procedure, and Optional.) In that case the convention is deemed to be protected so that Access can be applied. (AI-107).
AI-1 is a hangover from Ada 2005. The essence of the problem is that the language is inconsistent regarding the pragma (now aspect) Pack. On the one hand the text of the RM regarding packing says that entities have to be squeezed up really tightly. On the other hand alignment properties, atomicity and so on ought to be respected. The revised text clarifies that Pack should not do anything that violates other requirements.
Finally, a bother with requeue is addressed by AI-90 (requeue has been the source of many bothers in the past so another one is not unexpected). This time the problem concerns pre- and postconditions. Suppose we have entries E1 and E2 with pre- and postconditions. And suppose that E1 does a requeue onto E2. The current text is unclear as to what exactly is checked and when. Do we avoid checking any postcondition on E1 and do we bypass any precondition on E2? Certainly not is the brief answer. Basically we require that the postcondition on E2 implies that on E1 by saying that they must fully conform. And moreover any precondition on E2 is indeed checked when the call is requeued. Remember that parameters are passed on unchanged and that the requeue statement does not have any explicit parameters itself.

Contents   Index   References   Previous   Next 
© 2016 John Barnes Informatics.