Compaq ACMS for OpenVMS
Writing Server Procedures


Previous Contents Index

2.4 Using Cancel Procedures

This section first discusses the traditional reasons for using cancel procedures. The section then provides guidelines for writing procedures to avoid using cancel procedures, describes situations in which cancel procedures are unavoidable, and explains how to use the $SETAST system service to avoid canceling a task during critical portions of a procedure. This section also describes the conditions under which cancel procedures are called and explains how to write a cancel procedure.

The traditional reasons for using cancel procedures are:

2.4.1 Guidelines for Avoiding Cancel Procedures

There are two important reasons for avoiding cancel procedures. First, cancel procedures can adversely affect application performance. Also, it is often difficult to write a cancel procedure that performs the necessary cleanup operations, chiefly because an exception can be raised at any time while a task is executing. Therefore, it is recommended that wherever possible you avoid designing and writing tasks and step procedures that require server cancel procedures to clean up server processes following an exception.

To avoid writing cancel procedures, keep the following guidelines in mind as you design and write tasks and step procedures:

Section 2.4.3, discusses how to prevent the interruption of a procedure server while it is executing. Section 2.3 explains the conditions under which a procedure is run down.

2.4.2 Situations in Which Using Cancel Procedures Is Unavoidable

As mentioned earlier, whenever possible avoid designing and writing step procedures that require server cancel procedures. However, in certain circumstances, using a cancel procedure is unavoidable.

If you are using RDO in a distributed transaction, use a cancel procedure to roll back the default database transaction that Rdb starts automatically if the distributed transaction aborts between two RDO verbs. For example, the following segment of a BASIC step procedure reads and updates a control record in a database. If the distributed transaction times out and aborts immediately after the GET statement, then Rdb starts a default read-only transaction when it executes the next RDO statement (in this case, the MODIFY statement). See Chapter 4 for information on accessing an Rdb database using RDO statements.


    &RDB& START_TRANSACTION 
    &RDB&   DISTRIBUTED_TRANSACTION DISTRIBUTED_TID dist_tid 
    &RDB&   READ_WRITE RESERVING cust_control FOR SHARED WRITE 
 
    &RDB& FOR FIRST 1% c IN cust_control 
    &RDB&   GET 
    &RDB&       cust_num = c.next_cust_num 
    &RDB&   END_GET 
            next_cust_num = cust_num + 1% 
    &RDB&   MODIFY c USING 
    &RDB&       c.next_cust_num = next_cust_num 
    &RDB&   END_MODIFY 
    &RDB& END_FOR 

If you are using RMS in a distributed transaction, use a cancel procedure to unlock any records that a step procedure locks after the distributed transaction aborts. For example, the following segment of a BASIC step procedure reads and updates a control record in a file. If the distributed transaction times out and aborts immediately before the GET statement, then RMS is able to read the record successfully but returns an error when the step procedure executes the UPDATE operation. See Chapter 4 for information on accessing RMS files in distributed transactions.


    GET # emp_file,                                             & 
        KEY # 0 EQ emp_wksp::emp_badge_number,                  & 
        ALLOW NONE 
    MOVE TO # emp_file, emp_wksp 
    UPDATE # emp_file 

You must write a cancel procedure in other situations as well. If you are not using distributed transactions and you allow a server to remain active when a task is canceled between two processing steps while retaining context in a server, then:

See Section 2.4.6 for information on writing server cancel procedures.

2.4.3 Using $SETAST to Prevent Procedure Server Interruption

At times, procedures perform operations that, if interrupted, require cleanup. One way to avoid interrupting procedures (and avoid using cancel procedures) is to prevent ACMS from interrupting a step procedure during a critical section of code. To do this, you can use the $SETAST system service to disable delivery of asynchronous system traps (ASTs) at the beginning of the critical code. You can then reenable the delivery of ASTs at the end of the critical code. By using $SETAST, you can set a window in which ACMS cannot interrupt the step procedure if an exception is raised in the task.

Note

If you call the $SETAST system service to disable AST delivery in order to prevent ACMS from interrupting a step procedure, then you must ensure that you call the $SETAST system service to reenable AST delivery before the end of the step procedure. If you leave AST delivery disabled after the end of a step procedure, the server process can hang.

However, setting this window does not guarantee that an event such as a system crash does not interrupt the critical code. Also, this solution does not apply to programs running in DCL servers because the DCL server process handles all cancel requests in supervisor mode.

You must also be careful to avoid creating a situation in which a task cannot be canceled unless the server process is deleted. For example, if a step procedure disables AST delivery before acquiring an OpenVMS lock using the $ENQ service, ACMS cannot cancel the task until all of the following conditions are met:

In this example, if the process is never able to acquire the lock, then the task cancellation sequence can never complete because ACMS can never interrupt the server process. The only way to complete the task cancellation sequence is to delete the server process manually using the DCL STOP command.

To solve the problem of task cancellation, design the code in the step procedure carefully so that the procedure cannot stall indefinitely. You can, for example, use a timer to control how long the step procedure waits for the OpenVMS lock, as shown in Example 2-10.

Example 2-10 Pseudocode for Using$SETAST

Disable AST delivery 
Call $ENQ service in order to acquire an OpenVMS lock using an 
event flag and using an IOSB. 
If $ENQ returns SS$_NORMAL (we are waiting for the lock to be granted), 
then 
 
       Call the $SETIMR service to set a timer.  Pass the same event 
       flag that was passed to the $ENQ service. 
 
       Call the $WAITFR service to wait for the event flag passed to 
       the two services.  When this service finishes, it means that 
       either the $ENQ service has completed or the timer has expired. 
       To determine which has occurred, check the IOSB passed to the 
       $ENQ service.  If the status in the IOSB is non-zero, the $ENQ 
       service completed. 
 
If $ENQ service completed 
then     
       Cancel the timer 
else 
       Cancel lock request by calling $DEQ 
 
       Enable ASTs 
       Call ACMS$RAISE_NONREC_EXCEPTION to cancel the task 
 
If the $ENQ service completed unsuccessfully, 
then 
 
       Enable ASTs 
  
       Call ACMS$RAISE_NONREC_EXCEPTION to cancel the task 
 
else 
 
       Execute the critical code 
 
       Release the locking by calling the $DEQ service 
 
       Enable ASTs 

2.4.4 Conditions Under Which Cancel Procedures Are Called

Stated simply, ACMS calls the cancel procedure defined for each server in which a task is retaining context if either a transaction exception or a nonrecoverable exception is raised while a task is executing. ACMS does not call a server cancel procedure if a step exception is raised while a task is executing and the task handles the exception. If, however, a task does not handle a step exception, and a transaction exception or a nonrecoverable exception is raised as a result, ACMS calls a server cancel procedure, as stated. Compaq ACMS for OpenVMS Writing Applications, in its discussion of these conditions as they affect ADU syntax, includes specific examples of task definition syntax.

ACMS cancels procedures under the following conditions:

2.4.5 Cancel Procedures in Distributed and Nondistributed Transactions

ACMS calls server cancel procedures in a different order depending on whether a task uses distributed transactions, or a task uses either independent database transactions or RMS recovery units, or both.

Section 2.3 discusses the conditions under which ACMS runs down a server process.

2.4.6 Writing a Cancel Procedure

You declare a cancel procedure for a server in the ACMS procedure server definition within a task group. For example:


            . 
            . 
            . 
  SERVER IS 
    pers_upd_server: 
       . 
       . 
      CANCEL PROCEDURE IS pers_upd_server_can_proc; 
       . 
       . 
       . 
  END SERVER; 

ACMS calls the cancel procedure PERS_UPD_SERVER_CAN_PROC under the conditions discussed in Section 2.4.4.

The RUNDOWN ON CANCEL, RUNDOWN ON CANCEL IF INTERRUPTED, or NO RUNDOWN ON CANCEL clause in a server definition determines whether ACMS runs down the server process if a task is canceled while it has context in the server process. The default is RUNDOWN ON CANCEL. If a server definition declares RUNDOWN ON CANCEL, a cancel procedure is usually not necessary. However, if a server definition declares NO RUNDOWN ON CANCEL, then it is often necessary to use a cancel procedure. A cancel procedure may also be necessary if a server definition declares RUNDOWN ON CANCEL IF INTERRUPTED. For example:


   CANCEL PROCEDURE IS pers_upd_server_can_proc; 
   NO RUNDOWN ON CANCEL; 

The server definition initially determines whether or not the server process is run down on cancels. However, the return status of a cancel procedure overrides the server definition. ACMS provides three symbols that a cancel procedure can return:

Cancel procedures do not have access to workspaces. Store any information that might be needed by the cancel procedure in global variables while a step procedure is executing. The information is then available to the cancel procedure when it executes.

The following sections illustrate cancel procedures for a server accessing an Rdb database using RDO and for a server accessing an RMS file.

2.4.6.1 Cancel Procedure for Rdb with RDO

Write a server cancel procedure when you use Rdb with RDO in the following situations:

In Example 2-11, the procedure uses the ROLLBACK statement to roll back an active database transaction. Because there might not be a database transaction active every time the cancel procedure is called, the procedure ignores a transaction-not-active (RDB$_BAD_TRANS_HANDLE) error from the ROLLBACK statement. If any other error occurs, the procedure logs the error in the ACMS audit trail log by calling LIB$SIGNAL and returns the ACMS$_RNDWN status to force ACMS to run down the server process. If no errors are detected, the procedure returns the ACMS$_RNDWNIFINT status; in this case, ACMS runs down the server process only if the execution of a step procedure was interrupted due to the cancel.

Example 2-11 Server Cancel Procedure in BASIC Using Rdb with RDO

    FUNCTION LONG vr_update_cancel 
    !+ 
    ! Invoke database. 
    !- 
    &RDB& INVOKE DATABASE FILENAME "avertz_database:vehicle_rentals" 
 
    !+ 
    ! Cancel procedure return status. 
    !- 
    EXTERNAL LONG CONSTANT ACMS$_RNDWNIFINT 
    EXTERNAL LONG CONSTANT ACMS$_RNDWN 
 
    !+ 
    ! Rdb error ROLLBACK status code. 
    !- 
    EXTERNAL LONG CONSTANT RDB$_BAD_TRANS_HANDLE 
 
    !+ 
    ! Error logging routines 
    !- 
    EXTERNAL LONG FUNCTION LIB$SIGNAL 
    EXTERNAL LONG FUNCTION LIB$CALLG 
 
    !+ 
    ! Assume success. 
    !- 
    vr_update_cancel = ACMS$_RNDWNIFINT 
 
    !+ 
    ! ROLLBACK an outstanding database transaction. Ignore a 
    ! transaction-not-active error. For all other errors, log 
    ! the error and return ACMS$_RNDWN to ensure the server 
    ! runs down. 
    !- 
    &RDB& ROLLBACK 
    &RDB&   ON ERROR 
                IF Rdb$LU_STATUS <> RDB$_BAD_TRANS_HANDLE 
                THEN 
                    CALL LIB$CALLG( Rdb$MESSAGE_VECTOR,             & 
                                    LOC( LIB$SIGNAL ) BY VALUE ) 
                    vr_update_cancel = ACMS$_RNDWN 
                END IF 
    &RDB&   END_ERROR 
 
    END FUNCTION 


Previous Next Contents Index