Ada 95 Quality and Style Guide Chapter 6

Chapter 6: Concurrency - TOC - 6.3 TERMINATION

6.3.3 The Abort Statement

guideline

  • Avoid using the abort statement.
  • Consider using the asynchronous select statement rather than the abort statement.
  • Minimize uses of the asynchronous select statement.
  • Avoid assigning nonatomic global objects from a task or from the abortable part of an asynchronous select statement.

  • example

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

    The following example of asynchronous transfer of control shows a database transaction. The database operation may be cancelled (through a special input key) unless the commit transaction has begun. The code is extracted from the Rationale (1995, §9.4):

    with Ada.Finalization;
    package Txn_Pkg is
       type Txn_Status is (Incomplete, Failed, Succeeded);
       type Transaction is new Ada.Finalization.Limited_Controlled with private;
       procedure Finalize (Txn : in out transaction);
       procedure Set_Status (Txn    : in out Transaction;
                             Status : in     Txn_Status);
    private
       type Transaction is new Ada.Finalization.Limited_Controlled with
          record
             Status : Txn_Status := Incomplete;
             pragma Atomic (Status);
             . . . -- More components
          end record;
    end Txn_Pkg;
    -----------------------------------------------------------------------------
    package body Txn_Pkg is
       procedure Finalize (Txn : in out Transaction) is
       begin
          -- Finalization runs with abort and ATC deferred
          if Txn.Status = Succeeded then
             Commit (Txn);
          else
             Rollback (Txn);
          end if;
       end Finalize;
       . . . -- body of procedure Set_Status
    end Txn_Pkg;
    ----------------------------------------------------------------------------
    -- sample code block showing how Txn_Pkg could be used:
    declare
       Database_Txn : Transaction;
       -- declare a transaction, will commit or abort during finalization
    begin
       select  -- wait for a cancel key from the input device
          Input_Device.Wait_For_Cancel;
          -- the Status remains Incomplete, so that the transaction will not commit
       then abort  -- do the transaction
          begin
             Read (Database_Txn, . . .);
             Write (Database_Txn, . . .);
             . . .
             Set_Status (Database_Txn, Succeeded);
             -- set status to ensure the transaction is committed
          exception
             when others =>
                Ada.Text_IO.Put_Line ("Operation failed with unhandled exception:");
                Set_Status (Database_Txn, Failed);
          end;
       end select;
       -- Finalize on Database_Txn will be called here and, based on the recorded
       -- status, will either commit or abort the transaction.
    end;
    

    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 might 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 an abort completion 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 might not release processor resources as soon as you might expect. It also might not stop a runaway task because the task might be executing an infinite loop containing no abort completion points. There is no guarantee that a task will not abort until an abort completion point in multiprocessor systems, but the task will almost always stop running right away.

    An asynchronous select statement allows an external event to cause a task to begin execution at a new point, without having to abort and restart the task (Rationale 1995, §9.3). Because the triggering statement and the abortable statement execute in parallel until one of them completes and forces the other to be abandoned, you need only one thread of control. The asynchronous select statement improves maintainability because the abortable statements are clearly delimited and the transfer cannot be mistakenly redirected.

    In task bodies and in the abortable part of an asynchronous select, you should avoid assigning to nonatomic global objects, primarily because of the risk of an abort occurring before the nonatomic assignment completes. If you have one or more abort statements in your application and the assignment is disrupted, the target object can become abnormal, and subsequent uses of the object lead to erroneous execution (Ada Reference Manual 1995, §9.8). In the case of scalar objects, you can use the attribute 'Valid, but there is no equivalent attribute for nonscalar objects. (See Guideline 5.9.1 for a discussion of the 'Valid attribute.) You also can still safely assign to local objects and call operations of global protected objects.


    < Previous Page Search Contents Index Next Page >
    1 2 3 4 5 6 7 8 9 10 11
    TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC
    Appendix References Bibliography