Rationale for Ada 2012
1.3.4 Overview: Tasking and real-time facilities
There are a number of improvements regarding scheduling
and dispatching in the Real-Time Systems annex.
A small addition concerns
non-preemptive dispatching. In Ada 2005, a task wishing to indicate that
it is willing to be preempted has to execute
(or delay until Ada.Real_Time.Time_First
in Ravenscar). This is ugly and so a procedure Yield
is added to the package Ada.Dispatching
A further addition is the ability to indicate that
a task is willing to be preempted by a task of higher priority (but not
the same priority). This is done by calling Yield_To_Higher
which is declared in a new child package with specification
package Ada.Dispatching.Non_Preemptive is
procedure Yield_To_Same_Or_Higher renames Yield;
Another low-level scheduling capability concerns
suspension objects; these were introduced in Ada 95. Recall that we can
declare an object of type Suspension_Object
and call procedures to set it True
By calling Suspend_Until_True
a task can suspend
itself until the state of the object is true.
Ada 2005 introduced Earliest Deadline First (EDF)
scheduling. The key feature here is that tasks are scheduled according
to deadlines and not by priorities. A new facility introduced in Ada
2012 is the ability to suspend until a suspension object is true and
then set its deadline sometime in the future. This is done by calling
the aptly named procedure Suspend_Until_True_And_Set_Deadline
in a new child package Ada.Synchronous_Task_Control.EDF
A new scheduling feature is the introduction of synchronous
barriers in a new child package Ada.Synchronous_Barriers
The main features are a type Synchronous_Barrier
with a discriminant giving the number of tasks to be waited for.
type Synchronous_Barrier(Release_Threshold: Barrier_Limit)is limited private;
There is also a procedure
The_Barrier: in out Synchronous_Barrier;
Notified: out Boolean);
When a task calls Wait_For_Release
it gets suspended until the number waiting equals the discriminant. All
the tasks are then released and just one of them is told about it by
the parameter Notified being True.
The general idea is that this one task then does something on behalf
of all the others. The count of tasks waiting is then reset to zero so
that the synchronous barrier can be used again.
A number of other changes in this area are about
the use of multiprocessors and again concern the Real-Time Systems annex.
A new package System.Multiprocessors
is introduced as follows
package System.Multiprocessors is
type CPU_Rangeis range 0..implementation-defined;
Not_A_Specific_CPU: constant CPU_Range := 0:
subtype CPU is CPU_Range range 1 .. CPU_Range'Last;
function Number_Of_CPUs return CPU;
A value of subtype CPU
denotes a specific processor. Zero indicates don't know or perhaps don't
care. The total number of CPUs is determined by calling the function
. This is a function rather
than a constant because there could be several partitions with a different
number of CPUs on each partition.
Tasks can be allocated
to processors by an aspect specification. If we write
with CPU => 10;
will be executed
by processor number 10
. In the case of a task
type then all tasks of that type will be executed by the given processor.
The expression giving the processor for a task can be dynamic. The aspect
can also be set by a corresponding pragma CPU. (This is an example of
a pragma born obsolescent.) The aspect CPU
can also be given to the main subprogram in which case the expression
must be static.
Further facilities are provided by the child package
The idea is that processors are grouped together into dispatching domains.
A task may then be allocated to a domain and it will be executed on one
of the processors of that domain.
Domains are of a type Dispatching_Domain
They are created by a function Create
function Create(First, Last: CPU) return Dispatching_Domain;
that takes the first and last numbered CPU of the
domain and then returns the domain. All CPUs are initially in the System_Dispatching_Domain.
If we attempt to do something silly such as create overlapping domains,
then Dispatching_Domain_Error is raised.
Tasks can be assigned
to a domain in two ways. One way is to use an aspect
with Dispatching_Domain => My_Domain;
The other way is by
calling the procedure Assign_Task whose specification
Domain: in out Dispatching_Domain;
CPU: in CPU_Range := Not_A_Specific_CPU;
T: in Task_Id := Current_Task);
There are a number of other subprograms for manipulating
domains and CPUs. An interesting one is Delay_Until_And_Set_CPU
which delays the calling task until a given real time and then sets the
The Ravenscar profile
is now defined to be permissible with multiprocessors. However, there
is a restriction that tasks may not change CPU. Accordingly the definition
of the profile now includes the following restriction
In order to clarify
the use of multiprocessors with group budgets the package Ada.Execution_Time.Group_Budgets
introduced in Ada 2005 is slightly modified. The type Group_Budget
(which in Ada 2005 is just tagged limited private) has a discriminant
in Ada 2012 giving the CPU thus
CPU: System.Multiprocessors.CPU :=
is tagged limited private;
This means that a group budget only applies to a
single processor. If a task in a group is executed on another processor
then the budget is not consumed. Note that the default value for CPU
is CPU'First which is always 1.
relating to times and budgets concerns interrupts. Two Boolean constants
are added to the packageAda.Execution_Time
constant Boolean := implementation-defined;
constant Boolean := implementation-defined;
The constant Interrupt_Clocks_Supported
indicates whether the time spent in interrupts is accounted for separately
from the tasks and then Separate_Interrupt_Clocks_Supported
indicates whether it is accounted for each interrupt individually.
There is also a function
function Clocks_For_Interrupts return CPU_Time;
This function gives the time used over all interrupts.
Calling it if Interrupt_Clocks_Supported is
false raises Program_Error.
A new child package
accounts for the interrupts separately if Separate_Interrupt_Clocks_Supported
package Ada.Execution_Time.Interrupts is
function Clock(Interrupt: Ada.Interrupts.Interrupt_Id)
function Supported(Interrupt: Ada.Interrupts.Interrupt_Id)
The function Supported
indicates whether the time for a particular interrupt is being monitored.
If it is then Clock returns the accumulated
time spent in that interrupt handler (otherwise it returns zero). However,
if the overall constant Separate_Interrupt_Clocks_Supported
is false then calling Clock for a particular
interrupt raises Program_Error.
Multiprocessors have an impact on shared variables.
The existing pragma Volatile (now the aspect
Volatile) requires access to be in memory
but this is strictly unnecessary. All we need is to ensure that reads
and writes occur in the right order. A new aspect Coherent
was considered but was rejected in favour of simply changing the definition
The final improvement in this section is in the core
language and concerns synchronized interfaces and requeue. The procedures
of a synchronized interface may be implemented by a procedure or entry
or by a protected procedure. However, in Ada 2005 it is not possible
to requeue on a procedure of a synchronized interface even if it is implemented
by an entry. This is a nuisance and prevents certain high level abstractions.
Accordingly, Ada 2012 has an aspect Synchronization
that takes one of By_Entry
. So we might write
type Server is synchronized interface;
procedure Q(S: in out Server; X: in Item);
with Synchronization => By_Entry;
and then we are assured that we are permitted to
perform a requeue on any implementation of Q.
As expected there are a number of consistency rules.
The aspect can also be applied to a task interface or to a protected
interface. But for a task interface it obviously cannot be By_Protected_Procedure.
In the case of inheritance, any Synchronization
property is inherited. Naturally, multiple aspect specifications must
be consistent. Thus Optional can be overridden
by By_Entry or by By_Protected_Procedure
but other combinations conflict and so are forbidden.
A related change is that if an entry is renamed as
a procedure then we can do a requeue using the procedure name. This was
not allowed in Ada 95 or Ada 2005.
© 2011, 2012, 2013 John Barnes Informatics.
Sponsored in part by: