Document revision date: 30 March 2001

## OpenVMS Programming Concepts Manual

Previous | Contents | Index

1. The call to the SYS$SETIMR system service requests an AST at 10 seconds from the current time.
   The daytim argument refers to the quadword, which must contain the time in system time (64-bit) format. For details on how this is accomplished, see Chapter 27. The astadr argument refers to TIME_AST, the address of the AST service routine.
   When the call to the system service completes, the process continues execution.
2. The timer expires in 10 seconds and notifies the system. The system interrupts execution of the process and gives control to the AST service routine.
3. The user routine TIME_AST handles the interrupt. When the AST routine completes, it issues a RET instruction to return control to the program. The program resumes execution at the point at which it was interrupted.

## 8.7 Delivering ASTs

This section describes the AST service routine, some conditions affecting AST delivery, and the affect of kernel threads on AST delivery. The order of an AST delivery is not deterministic. The order the ASTs are entered into the AST queue for delivery to the process is not related to the order the particular operations that included AST notification requests were queued.

### 8.7.1 The AST Service Routine

An AST service routine must be a separate procedure. The AST must use the standard call procedure, and the routine must return using a RET instruction. If the service routine modifies any registers other than the standard scratch registers, it must set the appropriate bits in the entry mask so that the contents of those registers are saved.

Because you cannot know when the AST service routine will begin executing, you must take care that when you write the AST service routine it does not modify any data or instructions used by the main procedure (unless, of course, that is its function).

On entry to the AST service routine, the arguments shown in Table 8-3 are passed.

Table 8-3 AST Arguments for VAX Systems and Alpha Systems
VAX System Arguments | Alpha System Arguments
--- | ---
AST parameter | AST parameter
R0 | R0
R1 | R1
PC | PC
PSL | PS

Registers R0 and R1, the program counter (PC), and the processor status longword (PSL) on VAX systems, or processor status (PS) on Alpha systems were saved when the process was interrupted by delivery of the AST.

The AST parameter is an argument passed to the AST service routine so that it can identify the event that caused the AST. When you call a system service requesting an AST, or when you call the SYS$DCLAST system service, you can supply a value for the AST parameter. If you do not specify a value, the parameter defaults to 0.

The following example illustrates an AST service routine. In this example, the ASTs are queued by the SYS$DCLAST system service; the ASTs are delivered to the process immediately so that the service routine is called following each SYS$DCLAST system service call.

    #include <stdio.h>
    #include <ssdef.h>
    #include <starlet.h>
    #include <lib$routines.h>

    /* Declare the AST routine */

    void astrtn ( int );

    main()
    {
            unsigned int status, value1=1, value2=2;

            status = SYS$DCLAST(&astrtn,    /* astadr - AST routine */     (1)
                                value1,     /* astprm - AST parameter */
                                0);         /* acmode */
            if((status & 1) != 1)
                    LIB$SIGNAL( status );
       .
       .
       .
            status = SYS$DCLAST(&astrtn, value2, 0);
            if((status & 1) != 1)
                    LIB$SIGNAL( status );

    }

    void astrtn (int value) {                                (2)

    /* Evaluate AST parameter */
            switch (value)
            {
                    case 1: printf("Executing AST routine with value 1...\n");
                                    goto handler_1;
                                    break;

                    case 2: printf("Executing AST routine with value 2...\n");
                                    goto handler_2;
                                    break;

                    default: printf("Error\n");

            };

    /* Handle first AST */

    handler_1:
       .
       .
       .
            return;

    /* Handle second AST */

    handler_2:
       .
       .
       .
            return;
    }

1. The program calls the SYS$DCLAST AST system service twice to queue ASTs. Both ASTs specify the AST service routine, ASTRTN. However, a different parameter is passed for each call.
2. The first action this AST routine takes is to check the AST parameter so that it can determine if the AST being delivered is the first or second one declared. The value of the AST parameter determines the flow of execution. If a number of different values are determining a number of different paths of execution, Compaq recommends that you use the VAX MACRO instruction CASE.

### 8.7.2 Conditions Affecting AST Delivery

When a condition causes an AST to be delivered, the system may not be able to deliver the AST to the process immediately. An AST cannot be delivered under any of the following conditions:

- An AST service routine is currently executing at the same or at a more privileged access mode.
  Because ASTs are implicitly disabled when an AST service routine executes, one AST routine cannot be interrupted by another AST routine declared for the same access mode. Only one AST can be running in any particular processor mode at any one time. If an AST is active in any particular processor mode, it blocks all the same and less privileged ASTs. An AST can, however, be interrupted for an AST declared for a more privileged access mode.
- AST delivery is explicitly disabled for the access mode.
  A process can disable the delivery of AST interrupts with the Set AST Enable (SYS$SETAST) system service. This service may be useful when a program is executing a sequence of instructions that should not be interrupted for the execution of an AST routine.
  On Alpha systems, SYS$SETAST is often used in a main program that shares data with an AST routine in order to block AST delivery while the program accesses the shared data.
- The process is executing or waiting at an access mode more privileged than that for which the AST is declared.
  For example, if a user-mode AST is declared as the result of a system service but the program is currently executing at a higher access mode (because of another system service call, for example), the AST is not delivered until the program is once again executing in user mode.

If an AST cannot be delivered when the interrupt occurs, the AST is queued until the conditions disabling delivery are removed. Queued ASTs are ordered by the access mode from which they were declared, with those declared from more privileged access modes at the front of the queue. If more than one AST is queued for an access mode, the ASTs are delivered in the order in which they are queued.

### 8.7.3 Kernel Threads AST Delivery (Alpha Only)

On Alpha systems with the kernel threads implementation, ASTs are associated with the kernel thread that initiates them, though it is not required that they execute on the thread that initiates them. The use of the kernel thread's PID in the asynchronous system trap control block (ACB) identifies the initiating thread. Associating an ACB with its initiating thread is required; the arrival of an AST is often the event that allows a thread, waiting on a flag or resource, to be made computable.

An AST, for example, may set a flag or make a resource available, and when the AST is completed, the thread continues its execution in non-AST mode and rechecks the wait condition. If the wait condition is satisfied, the thread continues; if not, the thread goes back into the wait queue.

On the other hand, if an AST executes on a kernel thread other than the one that initiated it, then when the AST completes, the kernel thread that initiated the AST must be made computable to ensure that it rechecks a waiting condition that may now be satisfied.

The queuing and delivery mechanisms of ASTs make a distinction between outer mode ASTs (user and supervisor modes), and inner mode ASTs (executive and kernel modes). This distinction is necessary because of the requirement to synchronize inner mode access.

With the kernel threads implementation, the standard process control block (PCB) AST queues now appear in the kernel thread block (KTB), so that each kernel thread may receive ASTs independently. These queues receive outer mode ASTs, which are delivered on the kernel thread that initiates them. The PCB has a new set of inner mode queues for inner mode ASTs that require the inner mode semaphore. With the creation of multiple kernel threads, inner mode ASTs are inserted in the PCB queues, and are delivered on whichever kernel thread holds the inner mode semaphore. Inner mode ASTs, which are explicitly declared as thread-safe, are inserted in the KTB queues, and are delivered on the kernel thread that initiates them.

If a thread manager declares a user AST callback, then user mode ASTs are delivered to the thread manager. The thread manager then is responsible for determining the context in which the AST should be executed.

There are significant programming considerations to be understood when mixing POSIX Threads Library with ASTs. For information about using POSIX Threads Library with ASTs, see the Guide to POSIX Threads Library.

#### 8.7.3.1 Outer Mode (User and Supervisor) Non-Serial Delivery of ASTs

Before kernel threads, AST routine code of a given mode has always been able to assume the following:

- It would be processed serially. It would not be interrupted or executed concurrently with any other AST of the same mode.
- It would be processed without same-mode, non-AST level code executing concurrently.

Further, before kernel threads, user mode code could safely access data that it knows is only used by other user mode, non-AST level routines without needing any synchronization mechanisms. The underlying assumption is that only one thread of user mode execution exists. If the current code stream is accessing the data, then by implication no other code stream can be accessing it.

After kernel threads, this assumed behavior of AST routines and user mode code is no longer valid. Multiple user-mode, non-AST level code streams can be executing at the same time. The use of any data that can be accessed by multiple user-mode code streams must be modified to become synchronized using the load-locked (LDx_L) and store-conditional (STx_C) instructions, or by using some other synchronization mechanism.

Kernel threads assumes that multiple threads of execution can be active at one time and includes outer mode ASTs. Within any given kernel thread, outer mode ASTs will still be delivered serially. Also, the kernel thread model allows any combination of multiple outer mode threads, or multiple outer mode ASTs. However, outer-mode AST routines, as well as non-AST outer-mode code, has to be aware that any data structure that can be accessed concurrently by outer-mode code, or by any other outer-mode AST must be protected by some form of synchronization.

Before kernel threads, same-mode ASTs executed in the order that they were queued. After kernel threads and within a single kernel thread, that still is true. However, it is not true process-wide. If two ACBs are queued to two different KTBs, whichever is scheduled first, executes first. There is no attempt to schedule kernel threads in such a way to correctly order ASTs that have been queued to them. The ASTs execute in any order and can, in fact, execute concurrently.

#### 8.7.3.2 Inner Mode (Executive and Kernel) AST Delivery

Before kernel threads, OpenVMS implemented AST preemptions in inner modes as follows:

- An executive mode AST can preempt non-AST executive mode processing.
- A kernel mode AST can preempt non-AST kernel mode processing, or any executive mode processing.
- A special kernel mode AST can preempt a normal kernel mode AST, non-AST kernel mode, or any executive mode.
- No ASTs can be delivered when interrupt priority level (IPL) is raised to 2 or above. Special kernel mode ASTs execute entirely at IPL 2 or above, which is what prevents other kernel mode ASTs from executing while the special kernel mode AST is active.

After kernel threads, in contrast to the preceeding list, kernel threads deliver any non thread-safe inner mode ASTs to the kernel thread that already owns the semaphore. If no thread currently owns the semaphore when the AST is queued, then the semaphore is acquired in SCH$QAST, and the owner is set to the target kernel thread for that AST. Subsequently queued ASTs see that thread as the semaphore owner and are delivered to that thread. This allows the PALcode and the hardware architecture to process all the AST preemption and ordering rules.

## 8.8 ASTs and Process Wait States

A process in a wait state can be interrupted for the delivery of an AST and the execution of an AST service routine. When the AST service routine completes execution, the process is returned to the wait state, if the condition that caused the wait is still in effect.

With the exception of suspended waits (SUSP) and suspended outswapped waits (SUSPO), any wait states can be interrupted.

### 8.8.1 Event Flag Waits

If a process is waiting for an event flag and is interrupted by an AST, the wait state is restored following execution of the AST service routine. If the flag is set at completion of the AST service routine (for example, by completion of an I/O operation), then the process continues execution when the AST service routine completes.

Event flags are described in Section 6.6 of Chapter 6.

### 8.8.2 Hibernation

A process can place itself in a wait state with the Hibernate (SYS$HIBER) system service. This state can be interrupted for the delivery of an AST. When the AST service routine completes execution, the process continues hibernation. The process can, however, "wake" itself in the AST service routine or be awakened either by another process or as the result of a timer-scheduled wakeup request. Then, it continues execution when the AST service routine completes.

Process suspension is another form of wait; however, a suspended process cannot be interrupted by an AST. Process hibernation and suspension are described in Chapter 4.

### 8.8.3 Resource Waits and Page Faults

When a process is executing an image, the system can place the process in a wait state until a required resource becomes available, or until a page in its virtual address space is paged into memory. These waits, which are generally transparent to the process, can also be interrupted for the delivery of an AST.

## 8.9 Examples of Using AST Services

The following is an example of a Compaq Fortran program that finds the process identification (PID) number of any user working on a particular disk and delivers an AST to a local routine that notifies the user that the disk is coming down:

     PROGRAM DISK_DOWN
     ! Implicit none
     ! Status variable
     INTEGER STATUS
     STRUCTURE /ITMLST/
      UNION
       MAP
        INTEGER*2 BUFLEN,
     2            CODE
        INTEGER*4 BUFADR,
     2            RETLENADR
       END MAP
              MAP
        INTEGER*4 END_LIST
       END MAP
      END UNION
     END STRUCTURE
     RECORD /ITMLST/ DVILIST(2),
     2               JPILIST(2)
     ! Information for GETDVI call
     INTEGER PID_BUF,
     2       PID_LEN
     ! Information for GETJPI call
     CHARACTER*7 TERM_NAME
     INTEGER TERM_LEN
     EXTERNAL DVI$_PID,
     2        JPI$_TERMINAL
     ! AST routine and flag
     INTEGER AST_FLAG
     PARAMETER (AST_FLAG = 2)
     EXTERNAL NOTIFY_USER

     INTEGER SYS$GETDVIW,
     2       SYS$GETJPI,
     2       SYS$WAITFR

     ! Set up for SYS$GETDVI
     DVILIST(1).BUFLEN = 4
     DVILIST(1).CODE   = %LOC(DVI$_PID)
     DVILIST(1).BUFADR = %LOC(PID_BUF)
     DVILIST(1).RETLENADR = %LOC(PID_LEN)
     DVILIST(2).END_LIST = 0
     ! Find PID number of process using SYS$DRIVE0
     STATUS = SYS$GETDVIW (,
     2                     ,
     2                     '_MTA0:',       ! device
     2                     DVILIST,        ! item list
     2                     ,,,)
     IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
     ! Get terminal name and fire AST
     JPILIST(1).CODE = %LOC(JPI$_TERMINAL)
     JPILIST(1).BUFLEN = 7
     JPILIST(1).BUFADR = %LOC(TERM_NAME)
     JPILIST(1).RETLENADR = %LOC(TERM_LEN)
     JPILIST(2).END_LIST = 0
     STATUS = SYS$GETJPI (,
     2                    PID_BUF,         !process id
     2                    ,
     2                    JPILIST,         !itemlist
     2                    ,
     2                    NOTIFY_USER,     !AST
     2                    TERM_NAME)       !AST arg
     IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))

     ! Ensure that AST was executed
     STATUS = SYS$WAITFR(%VAL(AST_FLAG))
     IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
     END

     SUBROUTINE NOTIFY_USER (TERM_STR)
     ! AST routine that broadcasts a message to TERMINAL
     ! Dummy argument
     CHARACTER*(*) TERM_STR
     CHARACTER*8 TERMINAL
     INTEGER LENGTH
     ! Status variable
     INTEGER STATUS
     CHARACTER*(*) MESSAGE
     PARAMETER (MESSAGE =
     2             'SYS$TAPE going down in 10 minutes')
     ! Flag to indicate AST executed
     INTEGER AST_FLAG

     ! Declare system routines
     INTRINSIC LEN
     INTEGER  SYS$BRDCST,
     2        SYS$SETEF
     EXTERNAL SYS$BRDCST,
     2        SYS$SETEF,
     2        LIB$SIGNAL
     ! Add underscore to device name
     LENGTH = LEN (TERM_STR)
     TERMINAL(2:LENGTH+1) = TERM_STR
     TERMINAL(1:1) = '_'

     ! Send message
     STATUS = SYS$BRDCST(MESSAGE,
     2                   TERMINAL(1:LENGTH+1))
     IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
     ! Set event flag
     STATUS = SYS$SETEF (%VAL(AST_FLAG))
     IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
     END

The following is an example of a C program setting up an AST:

    #module SETAST "SRH X1.0-000"
    #pragma builtins

    /*
    ** COPYRIGHT (c) 1992 BY
    ** COMPAQ COMPUTER CORPORATION, MAYNARD, MASSACHUSETTS.
    ** ALL RIGHTS RESERVED.
    **
    ** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
    ** ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
    ** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER
    ** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
    ** OTHER PERSON.  NO TITLE TO AND  OWNERSHIP OF THE  SOFTWARE IS  HEREBY
    ** TRANSFERRED.
    **
    ** THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE
    ** AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY COMPAQ COMPUTER
    ** CORPORATION.
    **
    ** COMPAQ ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS
    ** SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY COMPAQ.
    */

    /*
    **++
    **  Facility:
    **
    ** Examples
    **
    **  Version: V1.0
    **
    **  Abstract:
    **
    ** Example of working with the $SETAST call and ASTs.
    **
    **  Author:
    ** Steve Hoffman
    **
    **  Creation Date:  1-Jan-1990
    **
    **  Modification History:
    **--
    */
    /*
     *  AST and $SETAST demo
     *  raise the AST shields
     *  request an AST, parameter is 1
     *  request an AST, parameter is 2
     *  lower the shields
     *  <bing1><bing2>
     */
    main()
        {
        int retstat = 0;
        int bogus();
        int SYS$SETAST();
        int SYS$DCLAST();

        printf("\ndisabling\n");
        /*
         * $SETAST() returns SS$_WASSET and SS$_WASCLR depending
         * on the previous setting of the AST shield.  Watch out,
         * SS$_WASSET looks like a SUCCESSFUL SS$_ACCVIO.  (ie:
         * a debug EXAMINE/COND shows SS$_WASSET as the error
         * %SYSTEM-S-ACCVIO.  *Real* ACCVIO's never have the "-S-"
         *  code!)
         */
        retstat = SYS$SETAST( 0 );
        printf("\n  disable/ was: %d\n", retstat );

        retstat = SYS$DCLAST( bogus, 1, 0 );
        retstat = SYS$DCLAST( bogus, 2, 0 );
        printf("\ndclast %x\n", retstat );

        printf("\nenabling\n" );
        retstat = SYS$SETAST( 1 );

        /*
         *  and, since we just lowered the shields, the ASTs should hit
         *  in here somewhere....
         */
        printf("\n  enable/ was: %d\n", retstat );

        return( 1 );
        };

    /*
     *  and, here's the entire, sophisticated, twisted AST code...
     */
    bogus( astprm )
    int astprm;
        {
        printf("\nAST tripped.  ast parameter was 0x%x\n\n", astprm);
        return( 1 );
        };

Previous | Next | Contents | Index

| privacy and legal statement |
5841PRO_025.HTML