HP OpenVMS Systems

Content starts here

DEC Ada
Technical Overview and Comparison on DIGITAL Platforms


Previous Contents Index


Chapter 4
Tasking and Task-Related Features

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:

  • Taking data concurrently from several sources
  • Performing terminal input-output and, simultaneously, a series of calculations
  • Handling interrupts

There are differences in tasking between platforms, as follows:

4.1 Threads and DECthreads

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.

Note

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 VAX systems, at least 21 pages are allocated.
  • On OpenVMS Alpha and DIGITAL UNIX systems, at least one page is allocated.

On OpenVMS Alpha and DIGITAL UNIX systems, the task stack allocated for any DEC Ada task (including the main task) has the following characteristics:

  • Default working storage area of 32 kbytes for tasks with fixed-size stacks
    This area is used during normal task execution for the storing of variables, call frames, and so on.
  • Minimum (and default) top guard area of one page
    This area is one or more pages at the top of the stack. This area is inaccessible to the program and is designed to help detect stack overflow. Reading or writing the top guard area causes a hardware access violation (SS$_ACCVIO). In most cases, this violation is treated as the predefined exception STORAGE_ERROR.

4.2.3 Stack Overflow

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:

  • On VAX systems, no guard area is created.
  • On Alpha systems, a minimal guard area is created.

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:

  • First-in first-out (FIFO) with preemption is the default strategy on VAX systems. Tasks of equal priority are processed in FIFO order. A task is run until it suspends. When it later resumes, it is placed at the rear of the ready queue for its priority level.
  • Round-robin with preemption is the default on Alpha systems. Tasks of equal priority take turns at the processor. A task is run for a certain period of time and then placed at the rear of the ready queue for its priority level.

4.3.2 Controlling Task Scheduling

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:

  • Pragma PRIORITY
    A priority is associated with a task if a pragma PRIORITY appears in the corresponding task specification or in the outermost declarative part of a main subprogram.
  • Types and operations in the Ada package SET_TASK_PRIORITY

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:

  • By using interrupt entries to handle those signals defined in the DEC Ada packages SIGNAL_CONSTANTS or POSIX_SIGNALS
  • By managing signal behavior using operations provided in the DEC Ada POSIX bindings

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:

  • SIGVTALRM---could affect task scheduling, timing, and delay statements. It also could interfere with asynchronous input-output behavior.
  • SIGALRM---could affect delay statements.

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.

Table 4-1 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.

Table 4-2 Accuracy of the Real-Time Clock and Delay Statements
System Accuracy in Seconds
OpenVMS VAX 0.01
OpenVMS Alpha 0.001
ULTRIX 0.003906

4.6.3 Shared Variables

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:

  • In the absence of a pragma SHARED or VOLATILE, a variable that is read by a task is not written by another task until the reading task reaches a synchronization point.
  • Also in the absence of a pragma SHARED or VOLATILE, a variable that is written by a task is not read or written by another task until the writing task reaches a synchronization point.

Pragma SHARED

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:

  • On OpenVMS VAX systems, an interlocked instruction is executed.
  • On Alpha systems, a memory barrier instruction is executed before and after the variable is written.

DEC Ada ensures the indivisibility of reads and updates of variables specified by the pragma SHARED as follows:

  • On OpenVMS VAX systems, by allowing only those scalar or access variables whose storage size does not exceed a longword (32 bits).
    On Alpha systems, by allowing all scalar or access variables.
  • On OpenVMS VAX systems, by allocating longword-aligned longwords for all shared variables whose storage is larger than a byte.
    On Alpha systems, by naturally aligning all shared variables.

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:

  • IMPORT_OBJECT
  • INTERFACE_NAME
  • EXPORT_OBJECT
  • COMMON_OBJECT

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.

Note

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.

4.6.4 Abort Statements

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