D.5.1 Dynamic Priorities for Tasks
1
[This clause describes how the base priority of a
task can be modified or queried at run time.]
Static Semantics
2
The following language-defined
library package exists:
3/2
{
AI95-00362-01}
with System;
with Ada.Task_Identification;
-- See C.7.1
package Ada.Dynamic_Priorities
is
pragma Preelaborate(Dynamic_Priorities);
4
procedure Set_Priority(Priority :
in System.Any_Priority;
T :
in Ada.Task_Identification.Task_Id :=
Ada.Task_Identification.Current_Task);
5
function Get_Priority (T : Ada.Task_Identification.Task_Id :=
Ada.Task_Identification.Current_Task)
return System.Any_Priority;
6
end Ada.Dynamic_Priorities;
Dynamic Semantics
7
The procedure Set_Priority sets the base priority
of the specified task to the specified Priority value. Set_Priority has
no effect if the task is terminated.
8
The function Get_Priority returns T's current base
priority.
{Tasking_Error (raised by failure
of run-time check)} Tasking_Error is raised
if the task is terminated.
8.a
Reason: There is no harm in setting the
priority of a terminated task. A previous version of Ada 9X made this
a run-time error. However, there is little difference between setting
the priority of a terminated task, and setting the priority of a task
that is about to terminate very soon; neither case should be an error.
Furthermore, the run-time check is not necessarily feasible to implement
on all systems, since priority changes might be deferred due to inter-processor
communication overhead, so the error might not be detected until after
Set_Priority has returned.
8.b
However, we wish to allow implementations to
avoid storing “extra” information about terminated tasks.
Therefore, we make Get_Priority of a terminated task raise an exception;
the implementation need not continue to store the priority of a task
that has terminated.
9
{Program_Error (raised
by failure of run-time check)} Program_Error
is raised by Set_Priority and Get_Priority if T is equal to Null_Task_Id.
10/2
{
AI95-00188-02}
On a system with a single processor, the setting
of Setting the
task's
base priority
of a task T
to the new value
occurs immediately at the first
point when T is outside the execution of takes
place as soon as is practical but not while the task is performing
a protected action
. This setting occurs no later
then the next abort completion point of the task T (see 9.8).
10.a/2
Implementation Note: {
AI95-00188-02}
The priority change is immediate if the target
task is on a delay queue or a ready queue outside of a protected action.
However, consider when When Set_Priority
is called by a task T1 to set the priority of T2, if T2 is blocked, waiting
on an entry call queued on a protected object, the entry queue needs
to be reordered. Since T1 might have a priority that is higher than the
ceiling of the protected object, T1 cannot, in general, do the reordering.
One way to implement this is to wake T2 up and have T2 do the work. This
is similar to the disentangling of queues that needs to happen when a
high-priority task aborts a lower-priority task, which might have a call
queued on a protected object with a low ceiling.
We have an Implementation Permission in D.4
to allow this implementation. We could have required an immediate priority
change if on a ready queue during a protected action, but that would
have required extra checks for ceiling violations to meet Bounded (Run-Time)
Error requirements of D.3 and potentially could
cause a protected action to be abandoned in the middle (by raising Program_Error).
That seems bad.
10.b
Reason: A
previous version of Ada 9X made it a run-time error for a high-priority
task to set the priority of a lower-priority task that has a queued call
on a protected object with a low ceiling. This was changed because:
10.c
- The check was not feasible to
implement on all systems, since priority changes might be deferred due
to inter-processor communication overhead. The calling task would continue
to execute without finding out whether the operation succeeded or not.
10.d
- The run-time check would tend
to cause intermittent system failures — how is the caller supposed
to know whether the other task happens to have a queued call at any given
time? Consider for example an interrupt that needs to trigger a priority
change in some task. The interrupt handler could not safely call Set_Priority
without knowing exactly what the other task is doing, or without severely
restricting the ceilings used in the system. If the interrupt handler
wants to hand the job off to a third task whose job is to call Set_Priority,
this won't help, because one would normally want the third task to have
high priority.
Bounded (Run-Time) Errors
11/2
This paragraph was
deleted.{
AI95-00327-01}
{bounded error
(cause) [partial]} If a task is blocked
on a protected entry call, and the call is queued, it is a bounded error
to raise its base priority above the ceiling priority of the corresponding
protected object. When an entry call is cancelled, it is a bounded error
if the priority of the calling task is higher than the ceiling priority
of the corresponding protected object. {Program_Error
(raised by failure of run-time check)} In
either of these cases, either Program_Error is raised in the task that
called the entry, or its priority is temporarily lowered, or both, or
neither.
11.a/2
This paragraph
was deleted.Ramification: Note
that the error is “blamed” on the task that did the entry
call, not the task that called Set_Priority. This seems to make sense
for the case of a task blocked on a call, since if the Set_Priority had
happened a little bit sooner, before the task queued a call, the entry-calling
task would get the error. In the other case, there is no reason not to
raise the priority of a task that is executing in an abortable_part,
so long as its priority is lowered before it gets to the end and needs
to cancel the call. The priority might need to be lowered to allow it
to remove the call from the entry queue, in order to avoid violating
the ceiling. This seems relatively harmless, since there is an error,
and the task is about to start raising an exception anyway.
Erroneous Execution
12
{erroneous execution
(cause) [partial]} If any subprogram in
this package is called with a parameter T that specifies a task object
that no longer exists, the execution of the program is erroneous.
12.a
Ramification: Note that this rule overrides
the above rule saying that Program_Error is raised on Get_Priority of
a terminated task. If the task object still exists, and the task is terminated,
Get_Priority raises Program_Error. However, if the task object no longer
exists, calling Get_Priority causes erroneous execution.
Documentation Requirements
12.1/2
{
AI95-00188-02}
On a multiprocessor, the implementation shall document
any conditions that cause the completion of the setting of the priority
of a task to be delayed later than what is specified for a single processor.
12.a.1/2
Documentation Requirement:
Any conditions that cause the completion
of the setting of the priority of a task to be delayed for a multiprocessor.
Metrics
13
The implementation
shall document the following metric:
14
- The execution time of a call to Set_Priority,
for the nonpreempting case, in processor clock cycles. This is measured
for a call that modifies the priority of a ready task that is not running
(which cannot be the calling one), where the new base priority of the
affected task is lower than the active priority of the calling task,
and the affected task is not on any entry queue and is not executing
a protected operation.
14.a/2
Documentation Requirement:
The metrics for Set_Priority.
15/2
29 {
AI95-00321-01}
Setting a task's base priority affects task dispatching. First, it can
change the task's active priority. Second, under the
FIFO_Within_Priorities standard
task dispatching policy it always causes the task to move to the
tail of the ready queue corresponding to its active priority, even if
the new base priority is unchanged.
16
30 Under the priority queuing policy, setting
a task's base priority has an effect on a queued entry call if the task
is blocked waiting for the call. That is, setting the base priority of
a task causes the priority of a queued entry call from that task to be
updated and the call to be removed and then reinserted in the entry queue
at the new priority (see
D.4), unless the call
originated from the
triggering_statement of
an
asynchronous_select.
17
31 The effect of two or more Set_Priority
calls executed in parallel on the same task is defined as executing these
calls in some serial order.
17.a
18
32 The rule for when Tasking_Error is raised
for Set_Priority or Get_Priority is different from the rule for when
Tasking_Error is raised on an entry call (see
9.5.3).
In particular, setting or querying the priority of a completed or an
abnormal task is allowed, so long as the task is not yet terminated.
19
33 Changing the priorities of a set of
tasks can be performed by a series of calls to Set_Priority for each
task separately. For this to work reliably, it should be done within
a protected operation that has high enough ceiling priority to guarantee
that the operation completes without being preempted by any of the affected
tasks.
Extensions to Ada 95
19.a/2
{
AI95-00188-02}
{extensions to Ada 95} Amendment
Correction: Priority changes are now required to be done immediately
so long as the target task is not on an entry queue.
19.b/2
{
AI95-00362-01}
Dynamic_Priorities is now Preelaborated, so it
can be used in preelaborated units.
Wording Changes from Ada 95
19.c/2
{
AI95-00327-01}
This Ada 95 clause was turned into a subclause.
The paragraph numbers are the same as those for D.5
in Ada 95.
19.d/2
{
AI95-00321-01}
There is no “standard” policy anymore,
so that phrase was replaced by the name of a specific policy in the notes.
19.e/2
{
AI95-00327-01}
The bounded error for the priority of a task being
higher than the ceiling of an object it is currently in was moved to
D.3, so that it applies no matter how the situation
arises.