[Ada Information Clearinghouse]

Ada '83 Quality and Style:

Guidelines for Professional Programmers

Copyright 1989, 1991,1992 Software Productivity Consortium, Inc., Herndon, Virginia.

CHAPTER 6: Concurrency

6.3 Termination

The ability of tasks to interact with each other using Ada's intertask communication features makes it especially important to manage planned or unplanned (e.g., in response to a catastrophic exception condition) termination in a disciplined way. To do otherwise can lead to a proliferation of undesired and unpredictable side effects as a result of the termination of a single task.

Language Ref Manual references: 9.4 Task Dependence - Termination of Tasks

In this section...
6.3.1 Avoiding Termination
6.3.2 Normal Termination
6.3.3 The Abort Statement
6.3.4 Abnormal Termination
Summary of Guidelines from this section


6.3.1 Avoiding Termination

guideline

example

In the following example an exception raised using the primary sensor is used to change Mode to Degraded still allowing execution of the system.
... 
loop

   Recognize_Degraded_Mode: 
      begin
      
         if Mode = Primary then
         
            select 
               Current_Position_Primary.Request_New_Coordinates 
                        (X, Y);
                        
            or 
               delay 0.25; 
               -- Decide whether to switch modes; 
            end select;
            
         else  -- Mode = Degraded
         
            Current_Position_Backup.Request_New_Coordinates 
                     (X, Y); 
         end if;
         
         ... 
      exception 
         when Tasking_Error | Program_Error => 
            Mode := Degraded; 
      end Recognize_Degraded_Mode;
      
end loop; 
...

rationale

Allowing a task to terminate may not support the requirements of the system. Without an exception handler for the rendezvous within the main task loop, the functions of the task may not be performed.

note

The use of an exception handler is the only way to guarantee recovery from an entry call to an abnormal task. Use of the 'Terminated attribute to test a task's availability before making the entry call can introduce a race condition where the tested task fails after the test but before the entry call (see Guideline 6.2.3).

Language Ref Manual references: 9.5 Entries, Entry Calls, and Accept Statements, 11.2 Exception Handlers


6.3.2 Normal Termination

guideline

example

This task will never terminate:
--------------------------------------------------------------------- 
task body Message_Buffer is 
   ...
   
begin  -- Message_Buffer 
   loop
   
      select 
         when Head /= Tail => -- Circular buffer not empty 
            accept Retrieve (Value :    out Element) do 
               ... 
            end Retrieve; 
              
      or 
         when not ((Head  = Lower_Bound and then 
                    Tail  = Upper_Bound)    or else 
                   (Head /= Lower_Bound and then 
                    Tail  = Index'Pred(Head))    ) 
                 => -- Circular buffer not full
                 
            accept Store (Value : in     Element); 
      end select; 
   end loop;
   
... 
end Message_Buffer; 
---------------------------------------------------------------------

rationale

A nonterminating task is a task whose body consists of a nonterminating loop with no selective wait with terminate, or a task that is dependent on a library package. Execution of a subprogram or block containing a task cannot complete until the task terminates. Any task that calls a subprogram containing a nonterminating task will be delayed indefinitely.

The effect of unterminated tasks at the end of program execution is undefined. A task dependent on a library package cannot be forced to terminate using a selective wait construct with terminate alternative and should be terminated explicitly during program shutdown. One way to terminate tasks dependent on library packages is to provide them with exit entries. Have the main subprogram call the exit entry just before it terminates.

Execution of an accept statement or of a selective wait statement without an else part, a delay, or a terminate alternative cannot proceed if no task ever calls the entry(s) associated with that statement. This could result in deadlock. Following this guideline entails programming multiple termination points in the task body. A reader can easily "know where to look" for the normal termination points in a task body. The termination points are the end of the body's sequence of statements, and alternatives of select statements.

exceptions

If you are simulating a cyclic executive, you may need a scheduling task that does not terminate. It has been said that no real-time system should be programmed to terminate. This is extreme. Systematic shutdown of many real-time systems is a desirable safety feature.

If you are considering programming a task not to terminate, be certain that it is not a dependent of a block or subprogram from which the task's caller(s) will ever expect to return. Since entire programs can be candidates for reuse (see Chapter 8), note that the task (and whatever it depends upon) will not terminate. Also be certain that for any other task that you do wish to terminate, its termination does not await this task's termination. Reread and fully understand paragraph 9.4 of Department of Defense (1983) on "Task Dependence - Termination of Tasks."

Language Ref Manual references: 9.3 Task Execution - Task Activation, 9.5 Entries, Entry Calls, and Accept Statements, 9.7.1 Selective Waits


6.3.3 The Abort Statement

guideline

example

If required in the application, provide a task entry for orderly shutdown.

rationale

When an abort statement is executed, there is no way to know what the targeted task was doing beforehand. Data for which the target task is responsible may be left in an inconsistent state. The overall effect on the system of aborting a task in such an uncontrolled way requires careful analysis. The system design must ensure that all tasks depending on the aborted task can detect the termination and respond appropriately.

Tasks are not aborted until they reach a synchronization point such as beginning or end of elaboration, a delay statement, an accept statement, an entry call, a select statement, task allocation, or the execution of an exception handler. Consequently, the abort statement may not release processor resources as soon as you may expect. It also may not stop a runaway task because the task may be executing an infinite loop containing no synchronization points.

Language Ref Manual references: 9.1 Abort Statements


6.3.4 Abnormal Termination

guideline

example

This is one of many tasks updating the positions of blips on a radar screen. When started, it is given part of the name by which its parent knows it. Should it terminate due to an exception, it signals the fact in one of its parent's data structures.
--------------------------------------------------------------------- 
task body Track is

   My_Index : Track_Index; 
   Neutral  : Boolean    := True;
   
begin  -- Track

   select 
      accept Start (Who_Am_I : in     Track_Index) do 
         My_Index := Who_Am_I; 
      end Start; 
      Neutral := False; 
      ...
      
   or 
      terminate; 
   end select;
   
   ... 
exception 
   when others => 
      if not Neutral then 
         Station(My_Index).Status := Dead; 
      end if;
      
end Track; 
---------------------------------------------------------------------

rationale

A task will terminate if an exception is raised within it for which it has no handler. In such a case, the exception is not propagated outside of the task (unless it occurs during a rendezvous). The task simply dies with no notification to other tasks in the program. Therefore, providing exception handlers within the task, and especially a handler for others, ensures that a task can regain control after an exception occurs. If the task cannot proceed normally after handling an exception, this affords it the opportunity to shut itself down cleanly and to notify tasks responsible for error recovery necessitated by the abnormal termination of the task.

note

Do not use the task status to determine if a rendezvous can be made with the task. If task A is dependent on task B and task A checks the status flag before it rendezvous with task B, there is a potential that task B fails between the status test and the rendezvous. In this case, task A must provide an exception handler to handle the Tasking_Error exception raised by the call to an entry of an abnormal task (see Guideline 6.3.1).

Language Ref Manual references: 9.1 Abort Statements, 11.2 Exception Handlers, 11.4 Exception Handling


Back to document index