[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

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:

  • Cleaning up procedure execution
    This might involve closing a channel that was opened to a terminal or to a temporary work file.
  • Freeing non-transaction-based resources
    Any transaction-based resources, such as record locks, that a procedure acquires are automatically freed when a transaction ends, whether the transaction commits or aborts. However, if a procedure acquires resources outside a distributed transaction, then the server process must release those resources if the server is to remain active (and not run down) following an exception. Following are examples of situations in which you need to release resources:
    • Releasing locks
      For example, if a step procedure takes out a lock on a resource by calling the OpenVMS $ENQ lock manager service directly, then that lock must subsequently be released by a call to the $DEQ service. If a task executes to completion normally, then the step procedure that acquired the lock can release it; if the task retains context in the server, another step procedure called later can release the lock. However, if the task is canceled, then the server cancel procedure must free the lock if the server process is to remain active and if other task instances need to acquire the lock.
    • Freeing memory
      Another example is when a step procedure calls LIB$GET_VM to allocate memory for a task instance. Because the memory is required to execute only the current task instance, it must subsequently be freed by a call to LIB$FREE_VM. If a task executes to completion normally, then the step procedure that allocated memory can free memory; if the task retains context in the server, another step procedure called later on can free the allocated memory. However, if the task is canceled, then the server cancel procedure must free memory if the server process is to remain active. Failure to free memory eventually results in the server running out of virtual memory.
    • Closing channels
      As a third example, you might need to open a channel to a terminal in a step procedure. Although generally not recommended, some applications require a procedure running in a procedure server to perform terminal I/O. However, if the task is canceled while the procedure has a channel open to the terminal, ACMS cannot close the channel. For this reason, if a task uses a processing step that does terminal I/O from a procedure server, ACMS always runs down the server process if the task is canceled and the server still has channels open to the terminal device.
      To avoid ACMS running down the server, you can use a cancel procedure to close the channel to the terminal. Note that if any channels are left open to the terminal, then ACMS overrides both the return status of the cancel procedure and the NO RUNDOWN clause of the server definition. ACMS also cancels the task and runs down the server if a step procedure ends normally but leaves a channel still open to the terminal.
  • Rolling back an active database transaction or a recovery unit
    A cancel procedure is needed to roll back active database transactions or recovery units under the following conditions:
    • If the step procedures in the server directly control Rdb or DBMS database transactions or RMS recovery units
    • If the database transactions or recovery units do not participate in a distributed transaction controlled from the task definition
    • If a task is canceled while retaining context in the server between processing steps
    • If the server is not run down as a result of the task being canceled

    A cancel procedure is not necessary if the server is run down as a result of the task being canceled. This is because the database transaction or recovery unit is automatically rolled back as a result of the server process running down. However, it is more efficient to allow a server to remain active if a task is canceled while retaining context but not executing in the server. In this case, it is advantageous to use a cancel procedure to roll back the database transaction or recovery unit so that the server can then be used by another task instance. Note that the recommended option is to allow a server process to run down if ACMS is forced to interrupt a step procedure in order to process a task cancellation.

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:

  • Control database transactions and recovery units with transaction steps in the task definition.
    Resource managers automatically roll back active database transactions and recovery units participating in a distributed transaction if that transaction rolls back. Therefore, you do not need to write a cancel procedure to do this if all database transactions and recovery units participate in distributed transactions that are controlled by the task definition.

    Note

    When using Rdb with RDO, or RMS, you must use a cancel procedure if you allow a server process to remain active after a task cancellation. See Section 2.4.2 for more information.
  • Allow server processes to run down following an exception.
    In most cases, if an exception requires ACMS to interrupt a step procedure while it is executing, allowing the server process to run down as part of the exception-handling sequence has the advantage that OpenVMS automatically performs most, if not all, of the necessary cleanup operations. For example, when a process is run down, OpenVMS automatically closes any channels that are still open. Also, any locks currently owned by the process are freed.
  • Avoid operations that require cleanup.
    For example, if you use task workspaces to store data rather than allocate memory using LIB$GET_VM, then you do not need a cancel procedure to free memory allocated in this way.
    However, some applications may require that a step procedure acquire a nondistributed-transaction-based resource. For example, a step procedure may need to acquire an OpenVMS lock using the $ENQ service before performing a critical operation. Using the $DEQ service, the step procedure releases the lock as soon as the operation has been completed.
    When an exception is raised, ACMS immediately interrupts a server process. If the code is interrupted during the time that the server process has acquired the lock, the server may not have the opportunity to call the $DEQ service. Since the server is using an OpenVMS lock, OpenVMS automatically releases the lock when the server process runs down. However, if the server is using an application-specific mechanism to maintain locks on resources, then running down a server process on an exception does not solve the problem; the resource is still locked.

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:

  • If the first processing step starts a database transaction or a recovery unit that is ended in the second processing step, you must write a cancel procedure to roll back the database transaction or recovery unit before the server can be used by another task.
  • If the first processing step locks records in an RMS file outside a recovery unit and those records are unlocked by the second processing step, you must write a cancel procedure to unlock the records before the server can be used by another task.

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:

  • A lock is granted to the server process.
  • The step procedure completes the critical operation.
  • The $DEQ service releases the lock.
  • AST delivery is reenabled.

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:

  • A step exception is raised that is not handled by the task.
    ACMS does not call cancel procedures if a step exception is raised while a task is executing and the exception is handled by the task. However, if the step exception is not handled by the task, then a transaction or nonrecoverable exception is raised, and ACMS calls server cancel procedures.
  • A task is canceled from an action clause in a task definition.
    ACMS conditionally calls cancel procedures if a nonrecoverable exception is raised due to a task executing a CANCEL TASK clause. ACMS calls cancel procedures only if the task is maintaining context in one or more server processes when the nonrecoverable exception is raised.
    ACMS processes the server context action before it processes the CANCEL TASK clause. Therefore, if the server context action in the action clause is RELEASE SERVER CONTEXT [IF ACTIVE SERVER CONTEXT], the task will no longer have context in the server, so ACMS does not call server cancel procedures. However, if the server context action is RETAIN SERVER CONTEXT [IF ACTIVE SERVER CONTEXT] or NO SERVER CONTEXT ACTION, and the task has context in one or more servers, then ACMS calls cancel procedures.
  • A distributed transaction fails to prepare successfully.
    This transaction exception can occur only while a task is executing a COMMIT TRANSACTION clause. Because the server context action of a transaction step must be RELEASE SERVER CONTEXT [IF ACTIVE SERVER CONTEXT], ACMS does not call cancel procedures in this case.
  • Other transaction or nonrecoverable exceptions are raised.
    Excluding the exception conditions already described, ACMS always calls cancel procedures when a transaction or nonrecoverable exception is raised. For example, ACMS calls cancel procedures in any server in which the task is maintaining context:
    • If the transaction timeout specified for a task expires before a distributed transaction completes and a step procedure does not complete before ACMS interrupts it
    • If a user presses [Ctrl/Y] to cancel a task
    • If an operator uses the ACMS/CANCEL TASK command to cancel the task

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.

  • In nondistributed transactions
    The server cancel procedure defined for the server is called before executing a database transaction or recovery-unit action, such as COMMIT or ROLLBACK, as specified in the task definition.
  • In distributed transactions
    Due to the asynchronous nature of transaction aborts, you cannot predict when cancel procedures are called.

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:

  • ACMS$_RNDWN
    If the cancel procedure returns ACMS$_RNDWN, ACMS runs down the server process even if the server definition declared NO RUNDOWN. If a cancel procedure cannot release the resources allocated to the server, it can return ACMS$_RNDWN to ensure that ACMS runs down the process and releases the resources.
  • ACMS$_NRNDWN
    If the cancel procedure returns ACMS$_NRNDWN, under normal conditions ACMS does not run down a server, regardless of whether or not the task is executing in the server process at the time an exception is raised. However, ACMS always runs down a server process if a step procedure signals a fatal OpenVMS exception or leaves channels open to a terminal device.
  • ACMS$_RNDWNIFINT
    If a cancel procedure returns ACMS$_RNDWNIFINT, ACMS runs down the server process only if the task is executing in the server process at the time an exception is raised. If the task is only maintaining context in the server at the time an exception is raised, then the server process remains active.

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:

  • If you access an Rdb database using RDO in a distributed transaction
    If you allow the server to remain active when a task is canceled, you must use a cancel procedure to roll back the default database transaction that Rdb starts if a step procedure accesses the database after a distributed transaction aborts.
  • If you write a task that retains context in a server between two processing steps
    If you allow the server to remain active when a task is canceled between two processing steps, you must use a cancel procedure to roll back the database transaction started in the first processing step.

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