Category : Miscellaneous Language Source Code
Archive   : ADAREF.ZIP
Filename : LRM-09.DOC

 
Output of file : LRM-09.DOC contained in archive : ADAREF.ZIP

The Ada Joint Program Office does not guarantee the accuracy of this
file, as compared with the contents of ANSI/MIL-STD-1815A-1983,
the Reference Manual for the Ada* Programming Language. If errors or
discrepancies are found in this machine-readable version, please
forward comments via the Defense Data Network (DDN) to

[email protected]

or via conventional mail to

Ada Information Clearinghouse
3D139 (1211 S. Fern, C-107)
The Pentagon
Washington, D.C. 20301-3081.

-----------------------------------------------------------------------


Copyright 1980, 1982, 1983 owned by the United States Government as
represented by the Under Secretary of Defense, Research and
Engineering. All rights reserved. Provided that notice of copyright
is included on the first page, this document may be copied in its
entirety without alteration or as altered by (1) adding text that is
clearly marked as an insertion; (2) shading or highlighting existing
text; (3) deleting examples. Permission to publish other excerpts
should be obtained from the Ada Joint Program Office, OUSDRE (R&AT),
The Pentagon, Washington, DC 20301-2081, U.S.A.

Ada* is a registered trademark of the United States Government,
Department of Defense, Under Secretary for Research and Engineering.
Its use is administered by the Ada Joint Program Office (AJPO). In
all contexts, use of the term "Ada" should indicate conformance to the
standard. In keeping with policies on voluntary conformance, use of
the term Ada is equivalent to a voluntary statement of conformance to
the standard.

The use of the trademarked term Ada will be made freely available to
those who use it to indicate conformance to the standard and in
accordance with the following guidelines:

In any published material the first appearance of the term Ada
must be properly acknowledged and include the statement "Ada
is a registered trademark of the U.S. Government (Ada Joint
Program Office)."

Describing, advertising, or promoting a language processor
as an "Ada" processor is equivalent to making a voluntary
statement of conformance to ANSI/MIL-STD-1815A.

The term Ada may be used in describing language processors
which are not completely conforming or are not making a claim
of conformance provided that there is a precise, easily
visible statement of their non-conformance at the same
time and in the same context.

Uses of the term Ada other than those described above, including all
organizations, companies and product names incorporating or utilizing
the term Ada, need written authorization from the AJPO. Those persons
advertising or otherwise promoting a language processor asserted as
being a standard Ada processor for sale or public use are required to
provide the AJPO with evidence sufficient to demonstrate conformance
to the Ada standard.

Use of the trademark does not imply any endorsement or warranty of the
product by either U.S. DoD or ANSI.

The Department of Defense (DoD), as the trademark owner, will allow
others to use the Ada trademark free of charge and will not take
action to prevent use of the Ada trademark so long as the trademark is
used properly according to the above policy. Misuse of the trademark
may lead to legal action.

In the interest of information interchange, all users of this standard
are encouraged to contact the Ada Joint Program Office, Department of
Defense, OUSD(R&E), Washington, D.C. 20301, U.S.A. Users of the
trademark and those reprinting the standard are required to notify the
AJPO.


*Ada is a registered trademark of the U.S. Government
(Ada Joint Program Office).




9. Tasks
The execution of a program that does not contain a task is defined in terms
of a sequential execution of its actions, according to the rules described
in other chapters of this manual. These actions can be considered to be
executed by a single logical processor.
Tasks are entities whose executions proceed in parallel in the following
sense. Each task can be considered to be executed by a logical processor
of its own. Different tasks (different logical processors) proceed
independently, except at points where they synchronize.
Some tasks have entries. An entry of a task can be called by other tasks.
A task accepts a call of one of its entries by executing an accept
statement for the entry. Synchronization is achieved by rendezvous between
a task issuing an entry call and a task accepting the call. Some entries
have parameters; entry calls and accept statements for such entries are
the principal means of communicating values between tasks.
The properties of each task are defined by a corresponding task unit which
consists of a task specification and a task body. Task units are one of
the four forms of program unit of which programs can be composed. The
other forms are subprograms, packages and generic units. The properties of
task units, tasks, and entries, and the statements that affect the
interaction between tasks (that is, entry call statements, accept
statements, delay statements, select statements, and abort statements) are
described in this chapter. Note:
Parallel tasks (parallel logical processors) may be implemented on
multicomputers, multiprocessors, or with interleaved execution on a single
physical processor. On the other hand, whenever an implementation can
detect that the same effect can be guaranteed if parts of the actions of a
given task are executed by different physical processors acting in
parallel, it may choose to execute them in this way; in such a case,
several physical processors implement a single logical processor.
References: abort statement 9.10, accept statement 9.5, delay statement
9.6, entry 9.5, entry call statement 9.5, generic unit 12, package 7,
parameter in an entry call 9.5, program unit 6, rendezvous 9.5, select
statement 9.7, subprogram 6, task body 9.1, task specification 9.1
9.1 Task Specifications and Task Bodies
9 - 1
A task unit consists of a task specification and a task body. A task
specification that starts with the reserved words task type declares a
task type. The value of an object of a task type designates a task having
the entries, if any, that are declared in the task specification; these
entries are also called entries of this object. The execution of the task
is defined by the corresponding task body.
9 - 2
A task specification without the reserved word type defines a single task.
A task declaration with this form of specification is equivalent to the
declaration of an anonymous task type immediately followed by the
declaration of an object of the task type, and the task unit identifier
names the object. In the remainder of this chapter, explanations are given
in terms of task type declarations; the corresponding explanations for
single task declarations follow from the stated equivalence.
task_declaration ::= task_specification;
task_specification ::=
task [type] identifier [is
{entry_declaration}
{representation_clause}
end [task_simple_name]]
task_body ::=
task body task_simple_name is
[declarative_part]
begin
sequence_of_statements
[exception
exception_handler
{exception_handler}]
end [task_simple_name];
The simple name at the start of a task body must repeat the task unit
identifier. Similarly if a simple name appears at the end of the task
specification or body, it must repeat the task unit identifier. Within a
task body, the name of the corresponding task unit can also be used to
refer to the task object that designates the task currently executing the
body; furthermore, the use of this name as a type mark is not allowed
within the task unit itself.
For the elaboration of a task specification, entry declarations and
representation clauses, if any, are elaborated in the order given. Such
representation clauses only apply to the entries declared in the task
specification (see 13.5).
The elaboration of a task body has no other effect than to establish that
the body can from then on be used for the execution of tasks designated by
objects of the corresponding task type.
The execution of a task body is invoked by the activation of a task object
of the corresponding type (see 9.3). The optional exception handlers at
the end of a task body handle exceptions raised during the execution of the
sequence of statements of the task body (see 11.4).
Examples of specifications of task types:
task type RESOURCE is
entry SEIZE;
entry RELEASE;
end RESOURCE;
9 - 3
task type KEYBOARD_DRIVER is
entry READ (C : out CHARACTER);
entry WRITE(C : in CHARACTER);
end KEYBOARD_DRIVER;
9 - 4
Examples of specifications of single tasks:
task PRODUCER_CONSUMER is
entry READ (V : out ITEM);
entry WRITE(E : in ITEM);
end;
task CONTROLLER is
entry REQUEST(LEVEL)(D : ITEM); -- a family of entries
end CONTROLLER;
task USER; -- has no entries
Example of task specification and corresponding body:
task PROTECTED_ARRAY is
-- INDEX and ITEM are global types
entry READ (N : in INDEX; V : out ITEM);
entry WRITE(N : in INDEX; E : in ITEM);
end;
task body PROTECTED_ARRAY is
TABLE : array(INDEX) of ITEM := (INDEX => NULL_ITEM);
begin
loop
select
accept READ (N : in INDEX; V : out ITEM) do
V := TABLE(N);
end READ;
or
accept WRITE(N : in INDEX; E : in ITEM) do
TABLE(N) := E;
end WRITE;
end select;
end loop;
end PROTECTED_ARRAY; Note:
A task specification specifies the interface of tasks of the task type with
other tasks of the same or of different types, and also with the main
program.
References: declaration 3.1, declarative part 3.9, elaboration 3.9, entry
9.5, entry declaration 9.5, exception handler 11.2, identifier 2.3, main
program 10.1, object 3.2, object declaration 3.2.1, representation clause
13.1, reserved word 2.9, sequence of statements 5.1, simple name 4.1, type
3.3, type declaration 3.3.1
9.2 Task Types and Task Objects
9 - 5
A task type is a limited type (see 7.4.4). Hence neither assignment nor
the predefined comparison for equality and inequality are defined for
objects of task types; moreover, the mode out is not allowed for a formal
parameter whose type is a task type.
9 - 6
A task object is an object whose type is a task type. The value of a task
object designates a task that has the entries of the corresponding task
type, and whose execution is specified by the corresponding task body. If
a task object is the object, or a subcomponent of the object, declared by
an object declaration, then the value of the task object is defined by the
elaboration of the object declaration. If a task object is the object, or
a subcomponent of the object, created by the evaluation of an allocator,
then the value of the task object is defined by the evaluation of the
allocator. For all parameter modes, if an actual parameter designates a
task, the associated formal parameter designates the same task; the same
holds for a subcomponent of an actual parameter and the corresponding
subcomponent of the associated formal parameter; finally, the same holds
for generic parameters.
Examples:
CONTROL : RESOURCE;
TELETYPE : KEYBOARD_DRIVER;
POOL : array(1 .. 10) of KEYBOARD_DRIVER;
-- see also examples of declarations of single tasks in 9.1
Example of access type designating task objects:
type KEYBOARD is access KEYBOARD_DRIVER;
TERMINAL : KEYBOARD := new KEYBOARD_DRIVER;
Notes:
Since a task type is a limited type, it can appear as the definition of a
limited private type in a private part, and as a generic actual parameter
associated with a formal parameter whose type is a limited type. On the
other hand, the type of a generic formal parameter of mode in must not be a
limited type and hence cannot be a task type.
Task objects behave as constants (a task object always designates the same
task) since their values are implicitly defined either at declaration or
allocation, or by a parameter association, and since no assignment is
available. However the reserved word constant is not allowed in the
declaration of a task object since this would require an explicit
initialization. A task object that is a formal parameter of mode in is a
constant (as is any formal parameter of this mode).
If an application needs to store and exchange task identities, it can do so
by defining an access type designating the corresponding task objects and
by using access values for identification purposes (see above example).
Assignment is available for such an access type as for any access type.
Subtype declarations are allowed for task types as for other types, but
there are no constraints applicable to task types.
References: access type 3.8, actual parameter 6.4.1, allocator 4.8,
assignment 5.2, component declaration 3.7, composite type 3.3, constant
3.2.1, constant declaration 3.2.1, constraint 3.3, designate 3.8 9.1,
9 - 7
elaboration 3.9, entry 9.5, equality operator 4.5.2, formal parameter 6.2,
formal parameter mode 6.2, generic actual parameter 12.3, generic
association 12.3, generic formal parameter 12.1, generic formal parameter
mode 12.1.1, generic unit 12, inequality operator 4.5.2, initialization
3.2.1, limited type 7.4.4, object 3.2, object declaration 3.2.1, parameter
association 6.4, private part 7.2, private type 7.4, reserved word 2.9,
subcomponent 3.3, subprogram 6, subtype declaration 3.3.2, task body 9.1,
type 3.3
9 - 8
9.3 Task Execution - Task Activation
A task body defines the execution of any task that is designated by a task
object of the corresponding task type. The initial part of this execution
is called the activation of the task object, and also that of the
designated task; it consists of the elaboration of the declarative part,
if any, of the task body. The execution of different tasks, in particular
their activation, proceeds in parallel.
If an object declaration that declares a task object occurs immediately
within a declarative part, then the activation of the task object starts
after the elaboration of the declarative part (that is, after passing the
reserved word begin following the declarative part); similarly if such a
declaration occurs immediately within a package specification, the
activation starts after the elaboration of the declarative part of the
package body. The same holds for the activation of a task object that is a
subcomponent of an object declared immediately within a declarative part or
package specification. The first statement following the declarative part
is executed only after conclusion of the activation of these task objects.
Should an exception be raised by the activation of one of these tasks, that
task becomes a completed task (see 9.4); other tasks are not directly
affected. Should one of these tasks thus become completed during its
activation, the exception TASKING_ERROR is raised upon conclusion of the
activation of all of these tasks (whether successfully or not); the
exception is raised at a place that is immediately before the first
statement following the declarative part (immediately after the reserved
word begin). Should several of these tasks thus become completed during
their activation, the exception TASKING_ERROR is raised only once.
Should an exception be raised by the elaboration of a declarative part or
package specification, then any task that is created (directly or
indirectly) by this elaboration and that is not yet activated becomes
terminated and is therefore never activated (see section 9.4 for the
definition of a terminated task).
For the above rules, in any package body without statements, a null
statement is assumed. For any package without a package body, an implicit
package body containing a single null statement is assumed. If a package
without a package body is declared immediately within some program unit or
block statement, the implicit package body occurs at the end of the
declarative part of the program unit or block statement; if there are
several such packages, the order of the implicit package bodies is
undefined.
A task object that is the object, or a subcomponent of the object, created
by the evaluation of an allocator is activated by this evaluation. The
activation starts after any initialization for the object created by the
allocator; if several subcomponents are task objects, they are activated
in parallel. The access value designating such an object is returned by
the allocator only after the conclusion of these activations.
Should an exception be raised by the activation of one of these tasks, that
task becomes a completed task; other tasks are not directly affected.
9 - 9
Should one of these tasks thus become completed during its activation, the
exception TASKING_ERROR is raised upon conclusion of the activation of all
of these tasks (whether successfully or not); the exception is raised at
the place where the allocator is evaluated. Should several of these tasks
thus become completed during their activation, the exception TASKING_ERROR
is raised only once.
Should an exception be raised by the initialization of the object created
by an allocator (hence before the start of any activation), any task
designated by a subcomponent of this object becomes terminated and is
therefore never activated.
9 - 1 4
Example:
procedure P is
A, B : RESOURCE; -- elaborate the task objects A, B
C : RESOURCE; -- elaborate the task object C
begin
-- the tasks A, B, C are activated in parallel before the first statement
...
end;
Notes:
An entry of a task can be called before the task has been activated. If
several tasks are activated in parallel, the execution of any of these
tasks need not await the end of the activation of the other tasks. A task
may become completed during its activation either because of an exception
or because it is aborted (see 9.10).
References: allocator 4.8, completed task 9.4, declarative part 3.9,
elaboration 3.9, entry 9.5, exception 11, handling an exception 11.4,
package body 7.1, parallel execution 9, statement 5, subcomponent 3.3, task
body 9.1, task object 9.2, task termination 9.4, task type 9.1,
tasking_error exception 11.1
9.4 Task Dependence - Termination of Tasks
Each task depends on at least one master. A master is a construct that is
either a task, a currently executing block statement or subprogram, or a
library package (a package declared within another program unit is not a
master). The dependence on a master is a direct dependence in the
following two cases:
(a) The task designated by a task object that is the object, or a
subcomponent of the object, created by the evaluation of an allocator
depends on the master that elaborates the corresponding access type
definition.
(b) The task designated by any other task object depends on the master
whose execution creates the task object.
Furthermore, if a task depends on a given master that is a block statement
executed by another master, then the task depends also on this other
master, in an indirect manner; the same holds if the given master is a
subprogram called by another master, and if the given master is a task that
depends (directly or indirectly) on another master. Dependences exist for
objects of a private type whose full declaration is in terms of a task type.
A task is said to have completed its execution when it has finished the
execution of the sequence of statements that appears after the reserved
9 - 11
word begin in the corresponding body. Similarly a block or a subprogram
is said to have completed its execution when it has finished the execution
of the corresponding sequence of statements. For a block statement, the
execution is also said to be completed when it reaches an exit, return, or
goto statement transferring control out of the block. For a procedure, the
execution is also said to be completed when a corresponding return
statement is reached. For a function, the execution is also said to be
completed after the evaluation of the result expression of a return
statement. Finally the execution of a task, block statement, or subprogram
is completed if an exception is raised by the execution of its sequence of
statements and there is no corresponding handler, or, if there is one, when
it has finished the execution of the corresponding handler.
9 - 12
If a task has no dependent task, its termination takes place when it has
completed its execution. After its termination, a task is said to be
terminated. If a task has dependent tasks, its termination takes place
when the execution of the task is completed and all dependent tasks are
terminated. A block statement or subprogram body whose execution is
completed is not left until all of its dependent tasks are terminated.
Termination of a task otherwise takes place if and only if its execution
has reached an open terminate alternative in a select statement (see
9.7.1), and the following conditions are satisfied:
- The task depends on some master whose execution is completed (hence
not a library package).
- Each task that depends on the master considered is either already
terminated or similarly waiting on an open terminate alternative of a
select statement.
When both conditions are satisfied, the task considered becomes terminated,
together with all tasks that depend on the master considered.
Example:
declare
type GLOBAL is access RESOURCE; -- see 9.1
A, B : RESOURCE;
G : GLOBAL;
begin
-- activation of A and B
declare
type LOCAL is access RESOURCE;
X : GLOBAL := new RESOURCE; -- activation of X.all
L : LOCAL := new RESOURCE; -- activation of L.all
C : RESOURCE;
begin
-- activation of C
G := X; -- both G and X designate the same task object
...
end; -- await termination of C and L.all (but not X.all)
...
end; -- await termination of A, B, and G.all
Notes:
The rules given for termination imply that all tasks that depend (directly
or indirectly) on a given master and that are not already terminated, can
be terminated (collectively) if and only if each of them is waiting on an
open terminate alternative of a select statement and the execution of the
given master is completed.
The usual rules apply to the main program. Consequently, termination of
the main program awaits termination of any dependent task even if the
corresponding task type is declared in a library package. On the other
hand, termination of the main program does not await termination of tasks
9 - 13
that depend on library packages; the language does not define whether such
tasks are required to terminate.
For an access type derived from another access type, the corresponding
access type definition is that of the parent type; the dependence is on
the master that elaborates the ultimate parent access type definition.
9 - 14
A renaming declaration defines a new name for an existing entity and hence
creates no further dependence.
References: access type 3.8, allocator 4.8, block statement 5.6,
declaration 3.1, designate 3.8 9.1, exception 11, exception handler 11.2,
exit statement 5.7, function 6.5, goto statement 5.9, library unit 10.1,
main program 10.1, object 3.2, open alternative 9.7.1, package 7, program
unit 6, renaming declaration 8.5, return statement 5.8, selective wait
9.7.1, sequence of statements 5.1, statement 5, subcomponent 3.3,
subprogram body 6.3, subprogram call 6.4, task body 9.1, task object 9.2,
terminate alternative 9.7.1
9.5 Entries, Entry Calls, and Accept Statements
Entry calls and accept statements are the primary means of synchronization
of tasks, and of communicating values between tasks. An entry declaration
is similar to a subprogram declaration and is only allowed in a task
specification. The actions to be performed when an entry is called are
specified by corresponding accept statements.
entry_declaration ::=
entry identifier [(discrete_range)] [formal_part];
entry_call_statement ::= entry_name [actual_parameter_part];
accept_statement ::=
accept entry_simple_name [(entry_index)] [formal_part] [do
sequence_of_statements
end [entry_simple_name]];
entry_index ::= expression
An entry declaration that includes a discrete range (see 3.6.1) declares a
family of distinct entries having the same formal part (if any); that is,
one such entry for each value of the discrete range. The term single entry
is used in the definition of any rule that applies to any entry other than
one of a family. The task designated by an object of a task type has (or
owns) the entries declared in the specification of the task type.
Within the body of a task, each of its single entries or entry families can
be named by the corresponding simple name. The name of an entry of a
family takes the form of an indexed component, the family simple name being
followed by the index in parentheses; the type of this index must be the
same as that of the discrete range in the corresponding entry family
declaration. Outside the body of a task an entry name has the form of a
selected component, whose prefix denotes the task object, and whose
selector is the simple name of one of its single entries or entry families.
A single entry overloads a subprogram, an enumeration literal, or another
single entry if they have the same identifier. Overloading is not defined
9 - 15
for entry families. A single entry or an entry of an entry family can be
renamed as a procedure as explained in section 8.5.
The parameter modes defined for parameters of the formal part of an entry
declaration are the same as for a subprogram declaration and have the same
meaning (see 6.2). The syntax of an entry call statement is similar to
that of a procedure call statement, and the rules for parameter
associations are the same as for subprogram calls (see 6.4.1 and 6.4.2).
9 - 16
An accept statement specifies the actions to be performed at a call of a
named entry (it can be an entry of a family). The formal part of an accept
statement must conform to the formal part given in the declaration of the
single entry or entry family named by the accept statement (see section
6.3.1 for the conformance rules). If a simple name appears at the end of
an accept statement, it must repeat that given at the start.
An accept statement for an entry of a given task is only allowed within the
corresponding task body; excluding within the body of any program unit
that is, itself, inner to the task body; and excluding within another
accept statement for either the same single entry or an entry of the same
family. (One consequence of this rule is that a task can execute accept
statements only for its own entries.) A task body can contain more than
one accept statement for the same entry.
For the elaboration of an entry declaration, the discrete range, if any, is
evaluated and the formal part, if any, is then elaborated as for a
subprogram declaration.
Execution of an accept statement starts with the evaluation of the entry
index (in the case of an entry of a family). Execution of an entry call
statement starts with the evaluation of the entry name; this is followed
by any evaluations required for actual parameters in the same manner as for
a subprogram call (see 6.4). Further execution of an accept statement and
of a corresponding entry call statement are synchronized.
If a given entry is called by only one task, there are two possibilities:
- If the calling task issues an entry call statement before a
corresponding accept statement is reached by the task owning the
entry, the execution of the calling task is suspended.
- If a task reaches an accept statement prior to any call of that entry,
the execution of the task is suspended until such a call is received.
When an entry has been called and a corresponding accept statement has been
reached, the sequence of statements, if any, of the accept statement is
executed by the called task (while the calling task remains suspended).
This interaction is called a rendezvous. Thereafter, the calling task and
the task owning the entry continue their execution in parallel.
If several tasks call the same entry before a corresponding accept
statement is reached, the calls are queued; there is one queue associated
with each entry. Each execution of an accept statement removes one call
from the queue. The calls are processed in the order of arrival.
An attempt to call an entry of a task that has completed its execution
raises the exception TASKING_ERROR at the point of the call, in the calling
task; similarly, this exception is raised at the point of the call if the
called task completes its execution before accepting the call (see also
9.10 for the case when the called task becomes abnormal). The exception
CONSTRAINT_ERROR is raised if the index of an entry of a family is not
within the specified discrete range.
9 - 17
Examples of entry declarations:
entry READ(V : out ITEM);
entry SEIZE;
entry REQUEST(LEVEL)(D : ITEM); -- a family of entries
Examples of entry calls:
CONTROL.RELEASE; -- see 9.2 and 9.1
PRODUCER_CONSUMER.WRITE(E); -- see 9.1
POOL(5).READ(NEXT_CHAR); -- see 9.2 and 9.1
CONTROLLER.REQUEST(LOW)(SOME_ITEM); -- see 9.1
9 - 18
Examples of accept statements:
accept SEIZE;
accept READ(V : out ITEM) do
V := LOCAL_ITEM;
end READ;
accept REQUEST(LOW)(D : ITEM) do
...
end REQUEST;
Notes:
The formal part given in an accept statement is not elaborated; it is only
used to identify the corresponding entry.
An accept statement can call subprograms that issue entry calls. An
accept statement need not have a sequence of statements even if the
corresponding entry has parameters. Equally, it can have a sequence of
statements even if the corresponding entry has no parameters. The sequence
of statements of an accept statement can include return statements. A task
can call its own entries but it will, of course, deadlock. The language
permits conditional and timed entry calls (see 9.7.2 and 9.7.3). The
language rules ensure that a task can only be in one entry queue at a given time.
If the bounds of the discrete range of an entry family are integer
literals, the index (in an entry name or accept statement) must be of the
predefined type INTEGER (see 3.6.1).
References: abnormal task 9.10, actual parameter part 6.4, completed task
9.4, conditional entry call 9.7.2, conformance rules 6.3.1,
constraint_error exception 11.1, designate 9.1, discrete range 3.6.1,
elaboration 3.1 3.9, enumeration literal 3.5.1, evaluation 4.5, expression
4.4, formal part 6.1, identifier 2.3, indexed component 4.1.1, integer type
3.5.4, name 4.1, object 3.2, overloading 6.6 8.7, parallel execution 9,
prefix 4.1, procedure 6, procedure call 6.4, renaming declaration 8.5,
return statement 5.8, scope 8.2, selected component 4.1.3, selector 4.1.3,
sequence of statements 5.1, simple expression 4.4, simple name 4.1,
subprogram 6, subprogram body 6.3, subprogram declaration 6.1, task 9, task
body 9.1, task specification 9.1, tasking_error exception 11.1, timed entry
call 9.7.3
9.6 Delay Statements, Duration, and Time
The execution of a delay statement evaluates the simple expression, and
suspends further execution of the task that executes the delay statement,
for at least the duration specified by the resulting value.
delay_statement ::= delay simple_expression;
9 - 19
The simple expression must be of the predefined fixed point type DURATION;
its value is expressed in seconds; a delay statement with a negative value
is equivalent to a delay statement with a zero value.
Any implementation of the type DURATION must allow representation of
durations (both positive and negative) up to at least 864
seconds (one
day); the smallest representable duration, DURATION'SMALL must not be
greater than twenty milliseconds (whenever possible, a value not greater
than fifty microseconds should be chosen). Note that DURATION'SMALL need
not correspond to the basic clock cycle, the named number SYSTEM.TICK (see
13.7).
9 - 2 4
The definition of the type TIME is provided in the predefined library
package CALENDAR. The function CLOCK returns the current value of TIME at
the time it is called. The functions YEAR, MONTH, DAY and SECONDS return
the corresponding values for a given value of the type TIME; the procedure
SPLIT returns all four corresponding values. Conversely, the function
TIME_OF combines a year number, a month number, a day number, and a
duration, into a value of type TIME. The operators "+" and "-" for
addition and subtraction of times and durations, and the relational
operators for times, have the conventional meaning.
The exception TIME_ERROR is raised by the function TIME_OF if the actual
parameters do not form a proper date. This exception is also raised by the
operators "+" and "-" if, for the given operands, these operators cannot
return a date whose year number is in the range of the corresponding
subtype, or if the operator "-" cannot return a result that is in the range
of the type DURATION.
package CALENDAR is
type TIME is private;
subtype YEAR_NUMBER is INTEGER range 1901 .. 2099;
subtype MONTH_NUMBER is INTEGER range 1 .. 12;
subtype DAY_NUMBER is INTEGER range 1 .. 31;
subtype DAY_DURATION is DURATION range 0.0 .. 86_4
;
function CLOCK return TIME;
function YEAR (DATE : TIME) return YEAR_NUMBER;
function MONTH (DATE : TIME) return MONTH_NUMBER;
function DAY (DATE : TIME) return DAY_NUMBER;
function SECONDS(DATE : TIME) return DAY_DURATION;
procedure SPLIT (DATE : in TIME;
YEAR : out YEAR_NUMBER;
MONTH : out MONTH_NUMBER;
DAY : out DAY_NUMBER;
SECONDS : out DAY_DURATION);
function TIME_OF(YEAR : YEAR_NUMBER;
MONTH : MONTH_NUMBER;
DAY : DAY_NUMBER;
SECONDS : DAY_DURATION := 0.0) return TIME;
function "+" (LEFT : TIME; RIGHT : DURATION) return TIME;
function "+" (LEFT : DURATION; RIGHT : TIME) return TIME;
function "-" (LEFT : TIME; RIGHT : DURATION) return TIME;
function "-" (LEFT : TIME; RIGHT : TIME) return DURATION;
function "<" (LEFT, RIGHT : TIME) return BOOLEAN;
function "<=" (LEFT, RIGHT : TIME) return BOOLEAN;
function ">" (LEFT, RIGHT : TIME) return BOOLEAN;
function ">=" (LEFT, RIGHT : TIME) return BOOLEAN;
9 - 21
TIME_ERROR : exception; -- can be raised by TIME_OF, "+", and "-"
private
-- implementation-dependent
end;
9 - 22
Examples:
delay 3.0; -- delay 3.0 seconds
declare
use CALENDAR;
-- INTERVAL is a global constant of type DURATION
NEXT_TIME : TIME := CLOCK + INTERVAL;
begin
loop
delay NEXT_TIME - CLOCK;
-- some actions
NEXT_TIME := NEXT_TIME + INTERVAL;
end loop;
end;
Notes:
The second example causes the loop to be repeated every INTERVAL seconds on
average. This interval between two successive iterations is only
approximate. However, there will be no cumulative drift as long as the
duration of each iteration is (sufficiently) less than INTERVAL.
References: adding operator 4.5, duration C, fixed point type 3.5.9,
function call 6.4, library unit 10.1, operator 4.5, package 7, private type
7.4, relational operator 4.5, simple expression 4.4, statement 5, task 9,
type 3.3
9.7 Select Statements
There are three forms of select statements. One form provides a selective
wait for one or more alternatives. The other two provide conditional and
timed entry calls.
select_statement ::= selective_wait
| conditional_entry_call | timed_entry_call
References: selective wait 9.7.1, conditional entry call 9.7.2, timed

entry call 9.7.3
9.7.1 Selective Waits
This form of the select statement allows a combination of waiting for, and
selecting from, one or more alternatives. The selection can depend on
conditions associated with each alternative of the selective wait.
9 - 23
selective_wait ::=
select
select_alternative
{or
select_alternative}
[else
sequence_of_statements]
end select;
select_alternative ::=
[when condition =>]
selective_wait_alternative
selective_wait_alternative ::= accept_alternative
| delay_alternative | terminate_alternative
accept_alternative ::= accept_statement [sequence_of_statements]
delay_alternative ::= delay_statement [sequence_of_statements]
terminate_alternative ::= terminate;
A selective wait must contain at least one accept alternative. In addition
a selective wait can contain either a terminate alternative (only one), or
one or more delay alternatives, or an else part; these three possibilities
are mutually exclusive.
A select alternative is said to be open if it does not start with when and
a condition, or if the condition is TRUE. It is said to be closed
otherwise.
For the execution of a selective wait, any conditions specified after when
are evaluated in some order that is not defined by the language; open
alternatives are thus determined. For an open delay alternative, the delay
expression is also evaluated. Similarly, for an open accept alternative
for an entry of a family, the entry index is also evaluated. Selection and
execution of one open alternative, or of the else part, then completes the
execution of the selective wait; the rules for this selection are
described below.
Open accept alternatives are first considered. Selection of one such
alternative takes place immediately if a corresponding rendezvous is
possible, that is, if there is a corresponding entry call issued by another
task and waiting to be accepted. If several alternatives can thus be
selected, one of them is selected arbitrarily (that is, the language does
not define which one). When such an alternative is selected, the
corresponding accept statement and possible subsequent statements are
executed. If no rendezvous is immediately possible and there is no else
part, the task waits until an open selective wait alternative can be
selected.
Selection of the other forms of alternative or of an else part is performed
as follows:
9 - 24
- An open delay alternative will be selected if no accept alternative
can be selected before the specified delay has elapsed (immediately,
for a negative or zero delay in the absence of queued entry calls);
any subsequent statements of the alternative are then executed. If
several delay alternatives can thus be selected (that is, if they have
the same delay), one of them is selected arbitrarily.
- The else part is selected and its statements are executed if no accept
alternative can be immediately selected, in particular, if all
alternatives are closed.
- An open terminate alternative is selected if the conditions stated in
section 9.4 are satisfied. It is a consequence of other rules that a
terminate alternative cannot be selected while there is a queued entry
call for any entry of the task.
9 - 25
The exception PROGRAM_ERROR is raised if all alternatives are closed and
there is no else part.
Examples of a select statement:
select
accept DRIVER_AWAKE_SIGNAL;
or
delay 30.0*SECONDS;
STOP_THE_TRAIN;
end select;
Example of a task body with a select statement:
task body RESOURCE is
BUSY : BOOLEAN := FALSE;
begin
loop
select
when not BUSY =>
accept SEIZE do
BUSY := TRUE;
end;
or
accept RELEASE do
BUSY := FALSE;
end;
or
terminate;
end select;
end loop;
end RESOURCE;
Notes:
A selective wait is allowed to have several open delay alternatives. A
selective wait is allowed to have several open accept alternatives for the
same entry.
References: accept statement 9.5, condition 5.3, declaration 3.1, delay
expression 9.6, delay statement 9.6, duration 9.6, entry 9.5, entry call
9.5, entry index 9.5, program_error exception 11.1, queued entry call 9.5,
rendezvous 9.5, select statement 9.7, sequence of statements 5.1, task 9
9.7.2 Conditional Entry Calls
A conditional entry call issues an entry call that is then canceled if a
rendezvous is not immediately possible.
conditional_entry_call ::=
select
9 - 26
entry_call_statement
[sequence_of_statements]
else
sequence_of_statements
end select;
9 - 27
For the execution of a conditional entry call, the entry name is first
evaluated. This is followed by any evaluations required for actual
parameters as in the case of a subprogram call (see 6.4).
The entry call is canceled if the execution of the called task has not
reached a point where it is ready to accept the call (that is, either an
accept statement for the corresponding entry, or a select statement with an
open accept alternative for the entry), or if there are prior queued entry
calls for this entry. If the called task has reached a select statement,
the entry call is canceled if an accept alternative for this entry is not
selected.
If the entry call is canceled, the statements of the else part are
executed. Otherwise, the rendezvous takes place; and the optional
sequence of statements after the entry call is then executed.
The execution of a conditional entry call raises the exception
TASKING_ERROR if the called task has already completed its execution (see
also 9.10 for the case when the called task becomes abnormal).
Example:
procedure SPIN(R : RESOURCE) is
begin
loop
select
R.SEIZE;
return;
else
null; -- busy waiting
end select;
end loop;
end;
References: abnormal task 9.10, accept statement 9.5, actual parameter
part 6.4, completed task 9.4, entry call statement 9.5, entry family 9.5,
entry index 9.5, evaluation 4.5, expression 4.4, open alternative 9.7.1,
queued entry call 9.5, rendezvous 9.5, select statement 9.7, sequence of
statements 5.1, task 9, tasking_error exception 11.1
9.7.3 Timed Entry Calls
A timed entry call issues an entry call that is canceled if a rendezvous is
not started within a given delay.
timed_entry_call ::=
select
entry_call_statement
[sequence_of_statements]
or
delay_alternative
9 - 28
end select;
9 - 29
For the execution of a timed entry call, the entry name is first evaluated.
This is followed by any evaluations required for actual parameters as in
the case of a subprogram call (see 6.4). The expression stating the delay
is then evaluated, and the entry call is finally issued.
If a rendezvous can be started within the specified duration (or
immediately, as for a conditional entry call, for a negative or zero
delay), it is performed and the optional sequence of statements after the
entry call is then executed. Otherwise, the entry call is canceled when
the specified duration has expired, and the optional sequence of statements
of the delay alternative is executed.
The execution of a timed entry call raises the exception TASKING_ERROR if
the called task completes its execution before accepting the call (see also
9.10 for the case when the called task becomes abnormal).
Example:
select
CONTROLLER.REQUEST(MEDIUM)(SOME_ITEM);
or
delay 45.0;
-- controller too busy, try something else
end select;
References: abnormal task 9.10, accept statement 9.5, actual parameter
part 6.4, completed task 9.4, conditional entry call 9.7.2, delay
expression 9.6, delay statement 9.6, duration 9.6, entry call statement
9.5, entry family 9.5, entry index 9.5, evaluation 4.5, expression 4.4,
rendezvous 9.5, sequence of statements 5.1, task 9, tasking_error exception 11.1
9.8 Priorities
Each task may (but need not) have a priority, which is a value of the
subtype PRIORITY (of the type INTEGER) declared in the predefined library
package SYSTEM (see 13.7). A lower value indicates a lower degree of
urgency; the range of priorities is implementation-defined. A priority is
associated with a task if a pragma
pragma PRIORITY (static_expression);
appears in the corresponding task specification; the priority is given by
the value of the expression. A priority is associated with the main
program if such a pragma appears in its outermost declarative part. At
most one such pragma can appear within a given task specification or for a
subprogram that is a library unit, and these are the only allowed places
for this pragma. A pragma PRIORITY has no effect if it occurs in a
subprogram other than the main program.
9 - 3 4
The specification of a priority is an indication given to assist the
implementation in the allocation of processing resources to parallel tasks
when there are more tasks eligible for execution than can be supported
simultaneously by the available processing resources. The effect of
priorities on scheduling is defined by the following rule:
If two tasks with different priorities are both eligible for execution
and could sensibly be executed using the same physical processors and
the same other processing resources, then it cannot be the case that
the task with the lower priority is executing while the task with the
higher priority is not.
9 - 31
For tasks of the same priority, the scheduling order is not defined by the
language. For tasks without explicit priority, the scheduling rules are
not defined, except when such tasks are engaged in a rendezvous. If the
priorities of both tasks engaged in a rendezvous are defined, the
rendezvous is executed with the higher of the two priorities. If only one
of the two priorities is defined, the rendezvous is executed with at least
that priority. If neither is defined, the priority of the rendezvous is
undefined.
Notes:
The priority of a task is static and therefore fixed. However, the
priority during a rendezvous is not necessarily static since it also
depends on the priority of the task calling the entry. Priorities should
be used only to indicate relative degrees of urgency; they should not be
used for task synchronization.
References: declarative part 3.9, entry call statement 9.5, integer type
3.5.4, main program 10.1, package system 13.7, pragma 2.8, rendezvous 9.5,
static expression 4.9, subtype 3.3, task 9, task specification 9.1
9.9 Task and Entry Attributes
For a task object or value T the following attributes are defined:
T'CALLABLE Yields the value FALSE when the execution of the task
designated by T is either completed or terminated, or when
the task is abnormal. Yields the value TRUE otherwise. The
value of this attribute is of the predefined type BOOLEAN.
T'TERMINATED Yields the value TRUE if the task designated by T is
terminated. Yields the value FALSE otherwise. The value of
this attribute is of the predefined type BOOLEAN.
In addition, the representation attributes STORAGE_SIZE, SIZE, and ADDRESS
are defined for a task object T or a task type T (see 13.7.2).
The attribute COUNT is defined for an entry E of a task unit T. The entry
can be either a single entry or an entry of a family (in either case the
name of the single entry or entry family can be either a simple or an
expanded name). This attribute is only allowed within the body of T, but
excluding within any program unit that is, itself, inner to the body of T.
E'COUNT Yields the number of entry calls presently queued on the
entry E (if the attribute is evaluated by the execution of an
accept statement for the entry E, the count does not include
the calling task). The value of this attribute is of the
type universal_integer. Note:
9 - 32
Algorithms interrogating the attribute E'COUNT should take precautions to
allow for the increase of the value of this attribute for incoming entry
calls, and its decrease, for example with timed entry calls.
References: abnormal task 9.10, accept statement 9.5, attribute 4.1.4,
boolean type 3.5.3, completed task 9.4, designate 9.1, entry 9.5, false
boolean value 3.5.3, queue of entry calls 9.5, storage unit 13.7, task 9,
task object 9.2, task type 9.1, terminated task 9.4, timed entry call
9.7.3, true boolean value 3.5.3, universal_integer type 3.5.4
9 - 33
9.10 Abort Statements
An abort statement causes one or more tasks to become abnormal, thus
preventing any further rendezvous with such tasks.
abort_statement ::= abort task_name {, task_name};
The determination of the type of each task name uses the fact that the type
of the name is a task type.
For the execution of an abort statement, the given task names are evaluated
in some order that is not defined by the language. Each named task then
becomes abnormal unless it is already terminated; similarly, any task that
depends on a named task becomes abnormal unless it is already terminated.
Any abnormal task whose execution is suspended at an accept statement, a
select statement, or a delay statement becomes completed; any abnormal
task whose execution is suspended at an entry call, and that is not yet in
a corresponding rendezvous, becomes completed and is removed from the entry
queue; any abnormal task that has not yet started its activation becomes
completed (and hence also terminated). This completes the execution of the
abort statement.
The completion of any other abnormal task need not happen before completion
of the abort statement. It must happen no later than when the abnormal
task reaches a synchronization point that is one of the following: the end
of its activation; a point where it causes the activation of another task;
an entry call; the start or the end of an accept statement; a select
statement; a delay statement; an exception handler; or an abort
statement. If a task that calls an entry becomes abnormal while in a
rendezvous, its termination does not take place before the completion of
the rendezvous (see 11.5).
The call of an entry of an abnormal task raises the exception TASKING_ERROR
at the place of the call. Similarly, the exception TASKING_ERROR is raised
for any task that has called an entry of an abnormal task, if the entry
call is still queued or if the rendezvous is not yet finished (whether the
entry call is an entry call statement, or a conditional or timed entry
call); the exception is raised no later than the completion of the
abnormal task. The value of the attribute CALLABLE is FALSE for any task
that is abnormal (or completed).
If the abnormal completion of a task takes place while the task updates a
variable, then the value of this variable is undefined. Example:
abort USER, TERMINAL.all, POOL(3);
Notes:
An abort statement should be used only in extremely severe situations
requiring unconditional termination. A task is allowed to abort any task,
including itself.
9 - 34
References: abnormal in rendezvous 11.5, accept statement 9.5, activation
9.3, attribute 4.1.4, callable (predefined attribute) 9.9, conditional
entry call 9.7.2, delay statement 9.6, dependent task 9.4, entry call
statement 9.5, evaluation of a name 4.1, exception handler 11.2, false
boolean value 3.5.3, name 4.1, queue of entry calls 9.5, rendezvous 9.5,
select statement 9.7, statement 5, task 9, tasking_error exception 11.1,
terminated task 9.4, timed entry call 9.7.3
9 - 35
9.11 Shared Variables
The normal means of communicating values between tasks is by entry calls
and accept statements.
If two tasks read or update a shared variable (that is, a variable
accessible by both), then neither of them may assume anything about the
order in which the other performs its operations, except at the points
where they synchronize. Two tasks are synchronized at the start and at the
end of their rendezvous. At the start and at the end of its activation, a
task is synchronized with the task that causes this activation. A task
that has completed its execution is synchronized with any other task.
For the actions performed by a program that uses shared variables, the
following assumptions can always be made:
- If between two synchronization points of a task, this task reads a
shared variable whose type is a scalar or access type, then the
variable is not updated by any other task at any time between these
two points.
- If between two synchronization points of a task, this task updates a
shared variable whose type is a scalar or access type, then the
variable is neither read nor updated by any other task at any time
between these two points.
The execution of the program is erroneous if any of these assumptions is
violated.
If a given task reads the value of a shared variable, the above assumptions
allow an implementation to maintain local copies of the value (for example,
in registers or in some other form of temporary storage); and for as long
as the given task neither reaches a synchronization point nor updates the
value of the shared variable, the above assumptions imply that, for the
given task, reading a local copy is equivalent to reading the shared
variable itself.
Similarly, if a given task updates the value of a shared variable, the
above assumptions allow an implementation to maintain a local copy of the
value, and to defer the effective store of the local copy into the shared
variable until a synchronization point, provided that every further read or
update of the variable by the given task is treated as a read or update of
the local copy. On the other hand, an implementation is not allowed to
introduce a store, unless this store would also be executed in the
canonical order (see 11.6).
The pragma SHARED can be used to specify that every read or update of a
variable is a synchronization point for that variable; that is, the above
assumptions always hold for the given variable (but not necessarily for
other variables). The form of this pragma is as follows:
pragma SHARED(variable_simple_name);
9 - 36
This pragma is allowed only for a variable declared by an object
declaration and whose type is a scalar or access type; the variable
declaration and the pragma must both occur (in this order) immediately
within the same declarative part or package specification; the pragma must
appear before any occurrence of the name of the variable, other than in an
address clause.
An implementation must restrict the objects for which the pragma SHARED is
allowed to objects for which each of direct reading and direct updating is
implemented as an indivisible operation.
References: accept statement 9.5, activation 9.3, assignment 5.2,
canonical order 11.6, declarative part 3.9, entry call statement 9.5,
erroneous 1.6, global 8.1, package specification 7.1, pragma 2.8, read a
value 6.2, rendezvous 9.5, simple name 3.1 4.1, task 9, type 3.3, update a
value 6.2, variable 3.2.1
9 - 37
9.12 Example of Tasking
The following example defines a buffering task to smooth variations between
the speed of output of a producing task and the speed of input of some
consuming task. For instance, the producing task may contain the
statements
loop
-- produce the next character CHAR
BUFFER.WRITE(CHAR);
exit when CHAR = ASCII.EOT;
end loop;
and the consuming task may contain the statements
loop
BUFFER.READ(CHAR);
-- consume the character CHAR
exit when CHAR = ASCII.EOT;
end loop;
The buffering task contains an internal pool of characters processed in a
round-robin fashion. The pool has two indices, an IN_INDEX denoting the
space for the next input character and an OUT_INDEX denoting the space for
the next output character.
task BUFFER is
entry READ (C : out CHARACTER);
entry WRITE(C : in CHARACTER);
end;
task body BUFFER is
POOL_SIZE : constant INTEGER := 1
058 POOL : array(1 .. POOL_SIZE) of CHARACTER;
COUNT : INTEGER range 0 .. POOL_SIZE := 0;
IN_INDEX, OUT_INDEX : INTEGER range 1 .. POOL_SIZE := 1;
begin
loop
select
when COUNT < POOL_SIZE =>
accept WRITE(C : in CHARACTER) do
POOL(IN_INDEX) := C;
end;
IN_INDEX := IN_INDEX mod POOL_SIZE + 1;
COUNT := COUNT + 1;
or when COUNT > 0 =>
accept READ(C : out CHARACTER) do
C := POOL(OUT_INDEX);
end;
OUT_INDEX := OUT_INDEX mod POOL_SIZE + 1;
COUNT := COUNT - 1;
or
terminate;
end select;
9 - 38
end loop;
end BUFFER;
9 - 39

  3 Responses to “Category : Miscellaneous Language Source Code
Archive   : ADAREF.ZIP
Filename : LRM-09.DOC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/