![]() |
![]() HP OpenVMS Systems |
![]() |
DEC Ada
|
Previous | Contents | Index |
An Ada task is an entity that executes in parallel with the execution of other tasks. The Ada language allows the declaration of both task types and task objects to perform tasks like the following:
There are differences in tasking between platforms, as follows:
On DIGITAL UNIX and OpenVMS Alpha systems, each Ada task (except a passive task) is implemented as a single stream of execution that is created and managed by the kernel. On these systems, DEC Ada tasking support is based on DECthreads, an implementation of the POSIX standard for threads.
DECthreads routines cannot be called from DEC Ada programs on VAX systems even though DECthreads is available on OpenVMS VAX systems. Attempts to call DECthreads routines do not work and are not supported. On VAX systems, the Ada Run-Time Library has its own threading system, and threading systems cannot be mixed. |
Although tasks are implemented as threads, all tasks in an Ada program are part of the same process. As a result, resources such as open files and virtual memory can be shared easily among tasks. Having all tasks in one process allows better integration with the programming environment (the shell and the debugger, for example).
Also, on OpenVMS Alpha and DIGITAL UNIX systems, DEC Ada tasks and foreign code that calls DECthreads routines can be used together.
The interaction between Ada tasks and DECthreads routines has some benefits. For example, if not on OpenVMS VAX, DEC Ada can call C code that is already threaded.
In general, DECthreads calls are supported. However, because of this interaction, direct calls to some DECthreads routines may not be supported in code running in the context of an Ada task.
Because all tasks in any Ada program currently run in the context of a single process, control can switch from one task to another quickly. This switch can occur at or during any of the several machine instructions that make up an Ada program statement. The switch can occur midway through the execution of an Ada source line.
Because of task switching and because of the need to control access to shared resources, it is necessary to synchronize the execution of tasks to get the desired behavior. The usual means of synchronizing tasks is to use Ada's rendezvous mechanism, which controls the interaction of tasks. The method of passing data parameters in a rendezvous is the same as for subprogram parameters.
In DEC Ada, the environment task that calls the main program is the
master of tasks that depend on library packages.
4.1.1 Assigning Task IDs
The DEC Ada Run-Time Library always assigns %TASK 1 to the environment task that executes the main program.
On OpenVMS VAX systems, it always assigns %TASK 0 to the null task that executes when there are no other tasks (including the main program) eligible to execute. The null task is a special task created by the run-time library, and you cannot apply most debugger commands to the null task. On OpenVMS Alpha and DIGITAL UNIX systems, %TASK 0 is often used for tasks that have been created but are not yet activated.
On VAX systems, task IDs are assigned at task creation. On OpenVMS
Alpha and DIGITAL UNIX systems, task IDs are assigned at activation.
4.1.2 Terminating Tasks
In accordance with the rules of task dependence and task termination, the environment task awaits termination of library-level tasks. In particular, the rules concerning terminate alternatives in select statements apply. In addition, input-output operations invoked from a task suspend the execution of only that task until they are completed. Other tasks can continue to execute.
In mixed-language programs, termination of Ada tasks takes place according to the usual rules. For example, termination of a non-Ada main program awaits termination of any Ada subprograms that involve tasks.
Tasks initiated in library packages follow the same rules for task
termination as other tasks. In particular, they are not terminated only
because the main program terminates. Terminate alternatives in
selective wait statements in library tasks are recommended.
4.2 Task Storage Allocation
Each task created requires storage. When programs create a task, a task
control block is allocated. When the task is activated, a new thread
(including a stack) is allocated. Both parts are heap allocated from
the process virtual address space. Because OpenVMS and DIGITAL UNIX are
virtual memory operating systems, the number of tasks that can be
created is limited only by the amount of virtual storage available to
the process.
4.2.1 Task Control Block
DEC Ada allocates the task control block when a task is created. This
block keeps track of that task's execution. DEC Ada deallocates the
task control block when execution leaves the scope of its master.
4.2.2 Task Stack
Each time a nonpassive task is activated, a task stack is allocated. The storage for the task stack is deallocated as soon as the task is terminated.
If you specify a size of zero (bytes) with T'STORAGE_SIZE, a default stack size is used. Also, regardless of the size specified, some additional space is allocated for task management purposes:
On OpenVMS Alpha and DIGITAL UNIX systems, the task stack allocated for any DEC Ada task (including the main task) has the following characteristics:
DEC Ada raises the exception STORAGE_ERROR when an attempt is made to
overflow either the main stack or an Ada task stack. The default stack
storage allocated for each non-Ada call should also be adequate
protection against stack overflow for most non-Ada routine calls.
4.2.4 Task-Related Storage Pragmas
On VAX systems, the pragma MAIN_STORAGE allows a fixed-size stack and stack storage areas to be specified for the environment (or main) task. It also causes the stack to be allocated in the P0 region rather than in the P1 region. This pragma is not available on Alpha systems.
The pragma TASK_STORAGE allows the specification of the size of the guard area for a task stack. (Because the guard area forms an area of memory that has no read or write access, it helps in the detection of stack overflow.)
If the pragma TASK_STORAGE specifies a value of zero (bytes), guard areas are created as follows:
In the absence of a pragma TASK_STORAGE, a default guard area is
created.
4.3 Task Scheduling
DEC Ada implements the Ada language requirement that when two tasks are eligible for execution and they have different priorities, the lower priority task does not execute while the higher priority task is waiting. The DEC Ada Run-Time Library keeps a task running until either the task is suspended or a higher priority task becomes ready.
On OpenVMS Alpha and DIGITAL UNIX systems, the operating system schedules Ada tasks along with other threads that are currently running. For example, if one program has nine tasks and another program has one task (all running concurrently at the same priority), DIGITAL UNIX allocates 90 percent of the CPU's resources to the program with nine tasks and 10 percent of the CPU's resources to the program with one task.
On OpenVMS VAX systems, the operating system issues the process and the
Ada RTL divides up tasks within the process.
4.3.1 Scheduling Strategies
Two scheduling strategies are available for DEC Ada tasks:
This section describes some ways to control task scheduling.
On OpenVMS and DIGITAL UNIX systems, DEC Ada provides the implementation-defined pragma TIME_SLICE that can be used to enable or disable round-robin scheduling of tasks with the same priority.
On OpenVMS VAX systems, the procedure SYSTEM_RUNTIME_TUNING.SET_TIME_SLICE is also available. (It is not available on Alpha systems.)
On OpenVMS VAX systems, the duration of a time slice can also be specified. When a positive, nonzero time slice is specified with either the pragma TIME_SLICE or the procedure SYSTEM_RUNTIME_TUNING.SET_TIME_SLICE, the default FIFO scheduling is changed to round-robin scheduling. Tasks of the same priority take turns at the processor for the specified amount of time (in seconds).
Pragma PRIORITY can be used to give more important tasks higher priorities.
Operations in the package SET_TASK_PRIORITY can be used.
On OpenVMS VAX systems, the duration of a time slice can also be specified.
On OpenVMS VAX systems, the debugger SET TASK/TIME_SLICE=t command allows the disabling of time slicing (SET TASK/TIME_SLICE=0.0) or the specification of a new value for a pragma TIME_SLICE. Therefore, this command can be used to time the execution of tasking programs or to diagnose problems that may be masked by the use of time slicing.
On OpenVMS VAX systems, there is an interaction between DEC Ada time slicing and the debugger watchpoint implementation. When watchpoints are set, the debugger can automatically increase the value of the pragma TIME_SLICE to 10.0. Slowing down the time-slice rate prevents these problems from occurring.
If round-robin scheduling is enabled, fairness is increased while increasing task switching overhead. In general, if time slicing is in effect, you are increasing responsiveness at the expense of more task-switching overhead. On OpenVMS VAX systems smaller values of the time-slice interval represent higher amounts of this overhead. Debugging is also more difficult. On OpenVMS VAX systems, time-slice values below 0.01 second do not result in faster time slicing because the smallest time increment supported by the OpenVMS operating system is 0.01 second.
In general, if time slicing is in effect, responsiveness is increased
at the expense of more task-switching overhead. On OpenVMS VAX systems,
smaller values of the time slice interval represent higher amounts of
this overhead.
4.3.3 Controlling Task Priorities
Each task may (but need not) have a priority, which is a value of the subtype PRIORITY. On all systems, the subtype PRIORITY has the following range where a lower value indicates a lower priority:
PRIORITY'FIRST | 0 |
PRIORITY'LAST | 15 |
On OpenVMS VAX systems, a task for which no pragma PRIORITY is specified has a default priority of 7. On Alpha systems, a task with an undefined priority competes fairly with other tasks, usually behaving as if it had a midrange priority (between 7 and 8).
DEC Ada provides the following two mechanisms for controlling task priorities:
On DIGITAL UNIX systems, DEC Ada allows task priorities to be changed
dynamically at run time to values of the subtype PRIORITY, as well as
to the values of DIGITAL UNIX real-time and system priorities.
4.4 External Interrupts (OpenVMS Systems)
On OpenVMS systems, an asynchronous system trap (AST) is an interrupt-like call initiated by the OpenVMS operating system in response to certain conditions or events detected by the operating system (or hardware) such as completion of an input-output request.
The identification of the routine to handle the AST is completely dynamic and is specified as one of the parameters to each system service call that provides this capability. Therefore, the static association of an interrupt with an entry does not apply to the OpenVMS operating system.
On OpenVMS systems, the pragma AST_ENTRY must be used to identify an entry that will potentially handle an AST. (This does not preclude its use in a normal entry call.) The pragma AST_ENTRY is allowed only in the same task type specification (or single task) as the entry to which it applies.
The pragma AST_ENTRY must be combined with the attribute AST_ENTRY, which is then used to identify the "routine" to handle the AST as part of a system service call.
Each evaluation of the AST_ENTRY attribute dynamically constructs a special code segment to transform the AST into an entry call. The evaluation returns the address of that segment to be passed to the OpenVMS operating system. When the AST occurs, the code segment queues an entry call to the intended entry and the AST is dismissed. Task execution then proceeds according to normal Ada rules. In particular, the rendezvous that results from the AST does not execute at AST level (interrupt level).
If the entry has a formal parameter, the parameter must be of a discrete, address or access type, and the parameter must be of mode in. When the AST occurs and the entry is called, the formal parameter receives the value of the astprm parameter provided by the system service.
OpenVMS system services that deliver ASTs can be called with the
subprograms provided in the DEC Ada package STARLET. (The package
TASKING_SERVICES also provides subprograms for calling those system
services that generate ASTs, but no AST handler can be provided by the
Ada program calling these operations.)
4.5 External Interrupts (DIGITAL UNIX and ULTRIX Systems)
On DIGITAL UNIX or ULTRIX systems, external interrupts can be associated with task entries through signals that are calls made by the operating system in response to certain events detected or caused by the operating system.
DIGITAL UNIX signals can be handled from Ada tasks in the following ways:
An address clause is used to identify an Ada task entry as one that can subsequently be called when the delivery of a DIGITAL UNIX signal occurs.
Certain ULTRIX signals are likely to interfere with Ada programs that use tasks. The following signals should be avoided or used with extra care:
On ULTRIX systems, the package SIGNAL_WAITING suspends a task's
execution while it waits to receive one or more of a number of ULTRIX
signals. The package declares a function, WAIT_FOR_SIGNAL, which
suspends a task until one of the specified signals is delivered. By
suspending only the calling task, the function WAIT_FOR_SIGNAL allows
other tasks to continue while signal waiting takes place.
4.6 Special Tasking Considerations
Use of tasks in an Ada program requires some care because, like any
other language construct, tasking has its own characteristic set of
programming pitfalls. The following sections discuss some such topics.
4.6.1 Passive Tasks
A passive task is a compiler optimization of an Ada task. Passive tasks behave exactly as unoptimized Ada tasks but yield performance improvements. Using passive tasks can improve significantly the performance of rendezvous in programs. A task rendezvous is accomplished with no context switching overhead.
Not all tasks are candidates to be made passive. A passive task must follow one of several sets of requirements.
On Alpha systems, DEC Ada provides the pragma PASSIVE to instruct the
compiler to make a task passive or to explicitly prevent the compiler
from making a task passive. This pragma is not available on OpenVMS VAX
systems.
4.6.2 Delay Statements and Related Issues
On OpenVMS, DEC Ada implements the delay statement as a call to the OpenVMS system service SYS$SETIMR. Each delay statement places an entry in the system timer queue, which, in turn, affects the OpenVMS Time Queue Entry Limit (TQELM) quota. Each delay statement also makes use of the SYS$SETIMR routine's ASTADR parameter, which specifies an AST routine. The use of delay statements can also affect (possibly exceed) the AST Queue Limit (ASTLM) quota.
On DIGITAL UNIX, DEC Ada implements delay statements using certain operating system calls. If certain DIGITAL UNIX signals are handled, the behavior of delay statements can be affected. For example, handling the SIGALRM signal affects delays in nontasking programs.
The execution of delay statements evaluates expressions of the fixed-point type DURATION. In DEC Ada, DURATION'SMALL is 2-14 seconds or approximately 61 microseconds. The value does not correspond to the value of the named number SYSTEM.TICK.
On OpenVMS VAX systems, the value of SYSTEM.TICK is 10.0-2 seconds or 10 milliseconds. (SYSTEM.TICK represents the smallest unit of time used by the OpenVMS VAX operating system in its time-related system services.)
On Alpha systems, the value of SYSTEM.TICK is 10.0-3 seconds or 1 millisecond. (On OpenVMS Alpha systems, SYSTEM.TICK represents the smallest unit of time used by the OpenVMS Alpha operating system in its time-related system services. On DIGITAL UNIX operating systems, SYSTEM.TICK represents the resolution of the DIGITAL UNIX system clock.)
Table 4-1 shows the properties of the type DURATION.
Attribute | Value | Comment |
---|---|---|
DURATION'DELTA | 0.0001 | |
DURATION'SMALL | 2.0 -14 | Approximately 61 microseconds |
DURATION'FIRST
DURATION'LAST |
--131072.0000
131071.9999 |
Approximately 36.4 hours |
DURATION'LARGE | 131071.9999 |
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 function TIME_OF combines a year number, a month number, a day number, and a duration into a value of type TIME.
On OpenVMS systems, the function CLOCK returns the current time and the type TIME is implemented as OpenVMS binary time.
On DIGITAL UNIX systems, the function CLOCK returns Greenwich time and the type TIME is implemented as a record type with two components. The first component expresses the number of days since 1 January 1901. The second component expresses the number of seconds that have passed during the current day. Table 4-2 shows the accuracy of the real-time clock and the delay statement on the various platforms.
System | Accuracy in Seconds |
---|---|
OpenVMS VAX | 0.01 |
OpenVMS Alpha | 0.001 |
ULTRIX | 0.003906 |
The code generated by an Ada compiler can store the value of a variable in several, one, or no places in the memory of the machine. The compiler believes, unless instructed otherwise, that it can detect all attempts to read or write a variable and arranges to have each of those attempts access the correct place.
The compiler makes assumptions based on the following rules:
The predefined pragma SHARED tells the compiler that any write to the declared variable must be made visible to reads by other tasks immediately, not just when the current task reaches a synchronization point. The implementation-defined pragma VOLATILE tells the compiler that any write by the current task to the specified variable must be made visible to reads by other tasks before the current task writes a variable for which the pragma SHARED was specified.
In implementing the pragma SHARED, DEC Ada guarantees that every read or update of a shared variable is a synchronization point. This is accomplished by ensuring the following actions for updates:
DEC Ada ensures the indivisibility of reads and updates of variables specified by the pragma SHARED as follows:
For the pragma SHARED, the variable must be of a scalar or access type. DEC Ada does not allow the pragma SHARED for objects of generic formal floating-point types and types subsequently derived. For the pragma VOLATILE, the variable can be of any type, including composite variables. If a variable is specified with the pragma VOLATILE, then any renaming of it, or any of its components, is also volatile.
Pragma VOLATILE
The implementation-defined pragma VOLATILE guarantees that a variable is allocated at a location in main memory from which the value is fetched and to which the value is updated on each use. Unlike the pragma SHARED, the pragma VOLATILE does not force multiple-CPU synchronization. The pragma VOLATILE can be used with variables of any type, including composite variables.
The pragma VOLATILE prevents the compiler from referring to an earlier read or write of the variable to deduce the variable's current value. Every read of the variable reads the variable itself, rather than a copy of the variable located in temporary storage. Likewise, every update of the variable updates the variable itself rather than a temporary copy. However, if the variable is in memory shared by two or more processes, each process may have its own cached copy of the variable. A write to this kind of variable must be synchronized by a rendezvous, a machine instruction, or a write to an object that has been specified with the pragma SHARED.
Unlike the pragma SHARED, the pragma VOLATILE does not guarantee indivisible access to the shared variable. In other words, it is possible to read partially updated values of the variable if other synchronization mechanisms (rendezvous, machine instructions, and so on) have not been used. Tasks that share a volatile variable must provide their own means of synchronizing their references.
Pragma VOLATILE is allowed only for a variable declared by an object declaration; the variable can be of any 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 or in one of the DEC Ada implementation-defined pragmas as follows:
The pragma VOLATILE must not occur in combination with the pragma SHARED.
The pragma VOLATILE guarantees that a variable is allocated at a location in main memory from which the value is fetched and to which the value is updated on each use. Unlike the pragma SHARED, the pragma VOLATILE does not force multiple-CPU synchronization.
On OpenVMS systems, any variable in DEC Ada that is asynchronously read or written by an OpenVMS system service must be specified with a pragma VOLATILE, or the program will be erroneous. |
The rules for an abort statement permit either an asynchronous or a synchronous implementation of abnormal task completion. An asynchronous implementation causes an abnormal task to become completed at arbitrary points in its execution (except where prohibited by the rules). A synchronous implementation causes an abnormal task to become completed only at specific points in its execution.
DEC Ada implements the abort statement in a synchronous rather than an asynchronous form. The synchronous form causes tasks to become completed only at specific points in their execution. Therefore, caution should be exercised in the use of abort statements. They should be used only when unconditional termination is required and only when it is safe to do so.
An abort statement causes one or more tasks to become abnormal, preventing any further rendezvous with such tasks. A means of ensuring the completion of an abnormal task at a particular point is to insert a delay 0.0 statement at that point.
Previous | Next | Contents | Index |