home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-11-08 | 56.0 KB | 1,374 lines |
- 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
-
-
- 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.
-
-
- 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;
-
- task type KEYBOARD_DRIVER is
- entry READ (C : out CHARACTER);
- entry WRITE(C : in CHARACTER);
- end KEYBOARD_DRIVER;
-
-
- 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
-
-
- 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.
-
-
- 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,
- 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.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.
- 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.
-
-
- 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
- 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.
-
-
- 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
- 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.
-
-
- 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
- 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).
-
-
- 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.
-
-
- 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
-
-
- 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;
-
-
- 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 86400 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).
-
-
- 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_400.0;
-
- 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;
-
- TIME_ERROR : exception; -- can be raised by TIME_OF, "+", and "-"
-
- private
- -- implementation-dependent
- end;
-
-
- 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.
-
-
- 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:
-
-
- - 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.
-
-
- 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
- entry_call_statement
- [sequence_of_statements]
- else
- sequence_of_statements
- end select;
-
-
- 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
- end select;
-
-
- 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.
-
-
- 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.
-
-
- 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:
-
-
- 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.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.
-
-
- 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.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);
-
-
- 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.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 := 100;
- 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;
- end loop;
- end BUFFER;