9.1 Task Units and Task Objects
This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue
A task unit is declared by a task declaration, which has a corresponding task_body
. A task declaration may be a task_type_declaration
, in which case it declares a named task type; alternatively, it may be a single_task_declaration
, in which case it defines an anonymous task type, as well as declaring a named task object of that type.
Syntax
2/3task_type_declaration
::=
task type defining_identifier
[known_discriminant_part
]
[aspect_specification
] [is
[new interface_list
with]
task_definition
];
3/5
single_task_declaration
::=
task defining_identifier
[aspect_specification
] [is
[new interface_list
with]
task_definition
];
4
task_definition
::=
{task_item
}
[ private
{task_item
}]
end [task_identifier
]
5/1
{8652/0009} task_item
::=
entry_declaration
| aspect_clause
6/3
task_body
::=
task body defining_identifier
[aspect_specification
] is
declarative_part
begin
handled_sequence_of_statements
end [task_identifier
];
7
If a task_identifier
appears at the end of a task_definition
or task_body
, it shall repeat the defining_identifier
.
Paragraph 8 was deleted.
Static Semantics
9A task_definition
defines a task type and its first subtype. The first list of task_item
s of a task_definition
, together with the known_discriminant_part
, if any, is called the visible part of the task unit. [ The optional list of task_item
s after the reserved word private is called the private part of the task unit.]
{8652/0029} For a task declaration without a task_definition
, a task_definition
without task_item
s is assumed.
For a task declaration with an interface_list
, the task type inherits user-defined primitive subprograms from each progenitor type (see 3.9.4), in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4). If the first parameter of a primitive inherited subprogram is of the task type or an access parameter designating the task type, and there is an entry_declaration
for a single entry with the same identifier within the task declaration, whose profile is type conformant with the prefixed view profile of the inherited subprogram, the inherited subprogram is said to be implemented by the conforming task entry using an implicitly declared nonabstract subprogram which has the same profile as the inherited subprogram and which overrides it.
The inherited subprograms can only come from an interface given as part of the task declaration.
The part about the implicitly declared subprogram is needed so that a subprogram implemented by an entry is considered to be overridden for the purpose of the other rules of the language. Without it, it would for instance be illegal for an abstract subprogram to be implemented by an entry, because the abstract subprogram would not be overridden. The Legality Rules below ensure that there is no conflict between the implicit overriding subprogram and a user-defined overriding subprogram.
Legality Rules
9.3/2A task declaration requires a completion[, which shall be a task_body
,] and every task_body
shall be the completion of some task declaration.
[Each interface_subtype_mark
of an interface_list
appearing within a task declaration shall denote a limited interface type that is not a protected interface.]
3.9.4 requires that an interface_list
only name interface types, and limits the descendants of the various kinds of interface types. Only a limited, task, or synchronized interface can have a task type descendant. Nonlimited or protected interfaces are not allowed, as they offer operations that a task does not have.
The prefixed view profile of an explicitly declared primitive subprogram of a tagged task type shall not be type conformant with any entry of the task type, if the subprogram has the same defining name as the entry and the first parameter of the subprogram is of the task type or is an access parameter designating the task type.
This prevents the existence of two operations with the same name and profile which could be called with a prefixed view. If the operation was inherited, this would be illegal by the following rules; this rule puts inherited and noninherited routines on the same footing. Note that this only applies to tagged task types (that is, those with an interface in their declaration); we do that as there is no problem with prefixed view calls of primitive operations for “normal” task types, and having this rule apply to all tasks would be incompatible with Ada 95.
For each primitive subprogram inherited by the type declared by a task declaration, at most one of the following shall apply:
- the inherited subprogram is overridden with a primitive subprogram of the task type, in which case the overriding subprogram shall be subtype conformant with the inherited subprogram and not abstract; or
- the inherited subprogram is implemented by a single entry of the task type; in which case its prefixed view profile shall be subtype conformant with that of the task entry.
An entry may implement two subprograms from the ancestors, one whose first parameter is of type T and one whose first parameter is of type access T. That doesn't cause implementation problems because “implemented by” (unlike “overridden’) probably entails the creation of wrappers.
If neither applies, the inherited subprogram shall be a null procedure. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit.
Each inherited subprogram can only have a single implementation (either from overriding a subprogram or implementing an entry), and must have an implementation unless the subprogram is a null procedure.
Dynamic Semantics
10[ The elaboration of a task declaration elaborates the task_definition
. The elaboration of a single_task_declaration
also creates an object of an (anonymous) task type.]
This is redundant with the general rules for the elaboration of a full_type_declaration
and an object_declaration
.
[The elaboration of a task_definition
creates the task type and its first subtype;] it also includes the elaboration of the entry_declaration
s in the given order.
{8652/0009} As part of the initialization of a task object, any aspect_clause
s and any per-object constraints associated with entry_declaration
s of the corresponding task_definition
are elaborated in the given order.
The only aspect_clause
s defined for task entries are ones that specify the Address of an entry, as part of defining an interrupt entry. These clearly need to be elaborated per-object, not per-type. Normally the address will be a function of a discriminant, if such an Address clause is in a task type rather than a single task declaration, though it could rely on a parameterless function that allocates sequential interrupt vectors.
We do not mention representation pragmas, since each pragma may have its own elaboration rules.
The elaboration of a task_body
has no effect other than to establish that tasks of the type can from then on be activated without failing the Elaboration_Check.
[The execution of a task_body
is invoked by the activation of a task of the corresponding type (see 9.2).]
The content of a task object of a given task type includes:
- The values of the discriminants of the task object, if any;
- An entry queue for each entry of the task object;
"For each entry" implies one queue for each single entry, plus one for each entry of each entry family.
- A representation of the state of the associated task.
NOTE 1 Other than in an access_definition
, the name of a task unit within the declaration or body of the task unit denotes the current instance of the unit (see 8.6), rather than the first subtype of the corresponding task type (and thus the name cannot be used as a subtype_mark
).
It can be used as a subtype_mark
in an anonymous access type. In addition, it is possible to refer to some other subtype of the task type within its body, presuming such a subtype has been declared between the task_type_declaration
and the task_body
.
NOTE 2 The notation of a selected_component
can be used to denote a discriminant of a task (see 4.1.3). Within a task unit, the name of a discriminant of the task type denotes the corresponding discriminant of the current instance of the unit.
NOTE 3 A task type is a limited type (see 7.5), and hence precludes use of assignment_statement
s and predefined equality operators. If a programmer wants to write an application that stores and exchanges task identities, they can do so by defining an access type designating the corresponding task objects and by using access values for identification purposes. Assignment is available for such an access type as for any access type. Alternatively, if the implementation supports the Systems Programming Annex, the Identity attribute can be used for task identification (see C.7.1).
Examples
22Examples of declarations of task types:
task type Server is
entry Next_Work_Item(WI : in Work_Item);
entry Shut_Down;
end Server;
24/2
task type Keyboard_Driver(ID : Keyboard_ID := New_ID) is
new Serial_Device with -- see 3.9.4
entry Read (C : out Character);
entry Write(C : in Character);
end Keyboard_Driver;
25
Examples of declarations of single tasks:
task Controller is
entry Request(Level)(D : Item); -- a family of entries
end Controller;
27
task Parser is
entry Next_Lexeme(L : in Lexical_Element);
entry Next_Action(A : out Parser_Action);
end;
28
task User; -- has no entries
29
Examples of task objects:
Agent : Server;
Teletype : Keyboard_Driver(TTY_ID);
Pool : array(1 .. 10) of Keyboard_Driver;
31
Example of access type designating task objects:
type Keyboard is access Keyboard_Driver;
Terminal : Keyboard := new Keyboard_Driver(Term_ID);
Extensions to Ada 83
The syntax rules for task declarations are modified to allow a known_discriminant_part
, and to allow a private part. They are also modified to allow entry_declaration
s and aspect_clause
s to be mixed.
Wording Changes from Ada 83
The syntax rules for tasks have been split up according to task types and single tasks. In particular: The syntax rules for task_declaration
and task_specification
are removed. The syntax rules for task_type_declaration
, single_task_declaration
, task_definition
and task_item
are new.
The syntax rule for task_body
now uses the nonterminal handled_sequence_of_statements
.
The declarative_part
of a task_body
is now required; that doesn't make any real difference, because a declarative_part
can be empty.
Extensions to Ada 95
Task types and single tasks can be derived from one or more interfaces. Entries of the task type can implement the primitive operations of an interface. Overriding_indicator
s can be used to specify whether or not an entry implements a primitive operation.
Wording Changes from Ada 95
{8652/0029} Corrigendum: Clarified that a task type has an implicit empty task_definition
if none is given.
{8652/0009} Corrigendum: Changed representation clauses to aspect clauses to reflect that they are used for more than just representation.
Revised the note on operations of task types to reflect that limited types do have an assignment operation, but not copying (assignment_statement
s).
Revised the note on use of the name of a task type within itself to reflect the exception for anonymous access types.
Extensions to Ada 2005
An optional aspect_specification
can be used in a task_type_declaration
, a single_task_declaration
, and a task_body
. This is described in 13.1.1.
Wording Changes from Ada 2005
Clarified that an inherited procedure of a progenitor is overridden when it is implemented by an entry.