Document revision date: 30 March 2001
[Compaq] [Go to the documentation home page] [How to order documentation] [Help on this site] [How to contact us]
[OpenVMS documentation]

OpenVMS Programming Concepts Manual


Previous Contents Index

28.4.1.4 Saving a Mapped File

To close a data file that was opened for user I/O, you must deassign the I/O channel assigned to that file. Before you can deassign a channel assigned to a mapped file, you must delete the virtual memory associated with the file (the memory used by the common block). When you delete the virtual memory used by a mapped file, any changes made while the file was mapped are written back to the disk file. Use the Delete Virtual Address Space (SYS$DELTVA) system service to delete the virtual memory used by a mapped file. Use the Deassign I/O Channel (SYS$DASSGN) system service to deassign the I/O channel assigned to a file.

The program segment shown in Example 28-4 closes a mapped file, automatically writing any modifications back to the disk. To ensure that the proper locations are deleted, pass SYS$DELTVA the addresses returned to your program by SYS$CRMPSC rather than the addresses you passed to SYS$CRMPSC. If you want to save modifications made to the mapped section without closing the file, use the Update Section File on Disk (SYS$UPDSEC) system service. To ensure that the proper locations are updated, pass SYS$UPDSEC the addresses returned to your program by SYS$CRMPSC rather than the addresses you passed to SYS$CRMPSC. Typically, you want to wait until the update operation completes before continuing program execution. Therefore, use the efn argument of SYS$UPDSEC to specify an event flag to be set when the update is complete, and wait for the system service to complete before continuing. For a complete description of the SYS$DELTVA, SYS$DASSGN, and SYS$UPDSEC system services, see the OpenVMS System Services Reference Manual.

Example 28-4 Closing a Mapped File

! Section address 
INTEGER*4 ADDR(2), 
2         RET_ADDR(2) 
! Event flag 
INTEGER*4 FLAG 
! Status block 
STRUCTURE /IO_BLOCK/ 
  INTEGER*2 IOSTAT, 
2           HARDWARE 
  INTEGER*4 BAD_PAGE 
END STRUCTURE 
RECORD /IO_BLOCK/ IOSTATUS 
   .
   .
   .
! Get an event flag 
STATUS = LIB$GET_EF (FLAG) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS)) 
! Update the section 
STATUS = SYS$UPDSEC (RET_ADDR, 
2                    ,,, 
2                    %VAL(FLAG) 
2                    , 
2                    IOSTATUS,,) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS)) 
! Wait for section to be updated 
STATUS = SYS$SYNCH (%VAL(FLAG), 
2                   IOSTATUS) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS)) 
   .
   .
   .

28.5 Opening and Updating a Sequential File

This section provides an example, written in Compaq Fortran, of how to open and update a sequential file on a VAX system. A sequential file consists of records arranged one after the other in the order in which they are written to the file. Records can only be added to the end of the file. Typically, sequential files are accessed sequentially.

Creating a Sequential File

To create a sequential file, use the OPEN statement and specify the following keywords and keyword values:

The file structure keyword ORGANIZATION also accepts the value 'INDEXED' or 'RELATIVE'.

Example 28-5 creates a sequential file of fixed-length records.

Example 28-5 Creating a Sequential File of Fixed-Length Records

   .
   .
   .
INTEGER STATUS, 
2       LUN, 
2       LIB$GET_INPUT, 
2       LIB$GET_LUN, 
2       STR$UPCASE 
INTEGER*2     FN_SIZE, 
2             REC_SIZE 
CHARACTER*256 FILENAME 
CHARACTER*80  RECORD 
! Get file name 
STATUS = LIB$GET_INPUT (FILENAME, 
2                       'File name: ', 
2                       FN_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Get free unit number 
STATUS = LIB$GET_LUN (LUN) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Open the file 
OPEN (UNIT = LUN, 
2     FILE = FILENAME (1:FN_SIZE), 
2     ORGANIZATION = 'SEQUENTIAL', 
2     ACCESS = 'SEQUENTIAL', 
2     RECORDTYPE = 'FIXED', 
2     FORM = 'UNFORMATTED', 
2     RECL = 20, 
2     STATUS = 'NEW') 
! Get the record input 
STATUS = LIB$GET_INPUT (RECORD, 
2                       'Input: ', 
2                       REC_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
DO WHILE (REC_SIZE .NE. 0) 
 
  ! Convert to uppercase 
  STATUS = STR$UPCASE (RECORD,RECORD) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
 
  WRITE  (UNIT=LUN) RECORD(1:REC_SIZE) 
  ! Get more record input 
  STATUS = LIB$GET_INPUT (RECORD, 
2                         'Input: ', 
2                         REC_SIZE) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
 
END DO 
 
END 

Updating a Sequential File

To update a sequential file, read each record from the file, update it, and write it to a new sequential file. Updated records cannot be written back as replacement records for the same sequential file from which they were read.

Example 28-6 updates a sequential file, giving the user the option of modifying a record before writing it to the new file. The same file name is used for both files; because the new update file was opened after the old file, the new file has a higher version number.

Example 28-6 Updating a Sequential File

   .
   .
   .
INTEGER STATUS, 
2       LUN1, 
2       LUN2, 
2       IOSTAT 
INTEGER*2  FN_SIZE 
CHARACTER*256 FILENAME 
CHARACTER*80 RECORD 
CHARACTER*80 NEW_RECORD 
INCLUDE '($FORDEF)' 
INTEGER*4 LIB$GET_INPUT, 
2         LIB$GET_LUN, 
2         STR$UPCASE 
! Get file name 
STATUS = LIB$GET_INPUT (FILENAME, 
2                       'File name: ', 
2                       FN_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Get free unit number 
STATUS = LIB$GET_LUN (LUN1) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Open the old file 
OPEN (UNIT=LUN1, 
2     FILE=FILENAME (1:FN_SIZE), 
2     ORGANIZATION='SEQUENTIAL', 
2     ACCESS='SEQUENTIAL', 
2     RECORDTYPE='FIXED', 
2     FORM='UNFORMATTED', 
2     RECL=20, 
2     STATUS='OLD') 
! Get free unit number 
STATUS = LIB$GET_LUN (LUN2) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Open the new file 
OPEN (UNIT=LUN2, 
2     FILE=FILENAME (1:FN_SIZE), 
2     ORGANIZATION='SEQUENTIAL', 
2     ACCESS='SEQUENTIAL', 
2     RECORDTYPE='FIXED', 
2     FORM='UNFORMATTED', 
2     RECL=20, 
2     STATUS='NEW') 
! Read a record from the old file 
READ (UNIT=LUN1, 
2     IOSTAT=IOSTAT) RECORD 
IF (IOSTAT .NE. IOSTAT_OK) THEN 
  CALL ERRSNS (,,,,STATUS) 
  IF (STATUS .NE. FOR$_ENDDURREA) THEN 
    CALL LIB$SIGNAL (%VAL(STATUS)) 
  END IF 
END IF 
 
DO WHILE (STATUS .NE. FOR$_ENDDURREA) 
 
  TYPE *, RECORD 
 
  ! Get record update 
  STATUS = LIB$GET_INPUT (NEW_RECORD, 
2                         'Update: ') 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  ! Convert to uppercase 
  STATUS = STR$UPCASE (NEW_RECORD, 
2                      NEW_RECORD) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
 
  ! Write unchanged record or updated record 
  IF (NEW_RECORD .EQ. ' ' ) THEN 
    WRITE (UNIT=LUN2) RECORD 
  ELSE 
    WRITE (UNIT=LUN2) NEW_RECORD 
  END IF 
 
  ! Read the next record 
  READ (UNIT=LUN1, 
2       IOSTAT=IOSTAT) RECORD 
  IF (IOSTAT .NE. IOSTAT_OK) THEN 
    CALL ERRSNS (,,,,STATUS) 
    IF (STATUS .NE. FOR$_ENDDURREA) THEN 
      CALL LIB$SIGNAL (%VAL(STATUS)) 
    END IF 
  END IF 
END DO 
 
END 

28.6 User-Open Routines

A user-open routine in Fortran, for example, gives you direct access to the file access block (FAB) and record access block (RAB) (the OpenVMS RMS structures that define file characteristics). Use a user-open routine to specify file characteristics that are otherwise unavailable from your programming language.

When you specify a user-open routine, you open the file rather than allow the program to open the file for you. Before passing the FAB and RAB to your user-open routine, any default file characteristics and characteristics that can be specified by keywords in the programming language are set. Your user-open routine should not set or modify such file characteristics because the language might not be aware that you have set the characteristics and might not perform as expected.

28.6.1 Opening a File

Section 28.4.1.2 provides guidelines on opening a file with a user-open routine. This section provides an example of a Fortran user-open routine.

28.6.1.1 Specifying USEROPEN

To open a file with a user-open routine, include the USEROPEN specifier in the Fortran OPEN statement. The value of the USEROPEN specifier is the name of the routine (not a character string containing the name). Declare the user-open routine as an INTEGER*4 function. Because the user-open routine name is specified as an argument, it must be declared in an EXTERNAL statement.

The following statement instructs Fortran to open SECTION.DAT using the routine UFO_OPEN:


! Logical unit number 
INTEGER LUN 
 
! Declare user-open routine 
INTEGER UFO_OPEN 
EXTERNAL UFO_OPEN 
   .
   .
   .
OPEN (UNIT = LUN, 
2     FILE = 'SECTION.DAT', 
2     STATUS = 'OLD', 
2     USEROPEN = UFO_OPEN) 
   .
   .
   .

28.6.1.2 Writing the User-Open Routine

Write a user-open routine as an INTEGER function that accepts three dummy arguments:

A user-open routine must perform at least the following operations. In addition, before opening the file, a user-open routine usually adjusts one or more fields in the FAB or the RAB or in both.

The following user-open routine opens an existing file. The file to be opened is specified in the OPEN statement of the invoking program unit.

UFO_OPEN.FOR


INTEGER FUNCTION UFO_OPEN (FAB, 
2                          RAB, 
2                          LUN) 
 
! Include Open VMS RMS definitions 
INCLUDE '($FABDEF)' 
INCLUDE '($RABDEF)' 
! Declare dummy arguments 
RECORD /FABDEF/ FAB 
RECORD /RABDEF/ RAB 
INTEGER LUN 
! Declare status variable 
INTEGER STATUS 
! Declare system routines 
INTEGER SYS$CREATE, 
2       SYS$OPEN, 
2       SYS$CONNECT 
! Optional FAB and/or RAB modifications 
   .
   .
   .
! Open file 
STATUS = SYS$OPEN (FAB) 
IF (STATUS) 
2  STATUS = SYS$CONNECT (RAB) 
 
! Return status of $OPEN or $CONNECT 
UFO_OPEN = STATUS 
 
END 

28.6.1.3 Setting FAB and RAB Fields

Each field in the FAB and RAB is identified by a symbolic name, such as FAB$L_FOP. Where separate bits in a field represent different attributes, each bit offset is identified by a similar symbolic name, such as FAB$V_CTG. The first three letters identify the structure containing the field. The letter following the dollar sign indicates either the length of the field (B for byte, W for word, or L for longword) or that the name is a bit offset (V for bit) rather than a field. The letters following the underscore identify the attribute associated with the field or bit. The symbol FAB$L_FOP identifies the FAB options field, which is a longword in length; the symbol FAB$V_CTG identifies the contiguity bit within the options field.

The STRUCTURE definitions for the FAB and RAB are in the $FABDEF and $RABDEF modules of the library SYS$LIBRARY:FORSYSDEF.TLB. To use these definitions, do the following:

  1. Include the modules in your program unit.
  2. Declare RECORD variables for the FAB and the RAB.
  3. Reference the various fields of the FAB and RAB using the symbolic name of the field.

The following user-open routine specifies that the blocks allocated for the file must be contiguous. To specify contiguity, you clear the best-try-contiguous bit (FAB$V_CBT) of the FAB$L_FOP field and set the contiguous bit (FAB$V_CTG) of the same field.

UFO_CONTIG.FOR


INTEGER FUNCTION UFO_CONTIG (FAB, 
2                            RAB, 
2                            LUN) 
 
! Include Open VMS RMS definitions 
INCLUDE '($FABDEF)' 
INCLUDE '($RABDEF)' 
! Declare dummy arguments 
RECORD /FABDEF/ FAB 
RECORD /RABDEF/ RAB 
INTEGER LUN 
! Declare status variable 
INTEGER STATUS 
! Declare system procedures 
INTEGER SYS$CREATE, 
2       SYS$CONNECT 
! Clear contiguous-best-try bit and 
! set contiguous bit in FAB options 
FAB.FAB$L_FOP = IBCLR (FAB.FAB$L_FOP, FAB$V_CBT) 
FAB.FAB$L_FOP = IBSET (FAB.FAB$L_FOP, FAB$V_CTG) 
! Open file 
STATUS = SYS$CREATE (FAB) 
IF (STATUS) STATUS = SYS$CONNECT (RAB) 
 
! Return status of open or connect 
UFO_CONTIG = STATUS 
 
END 


Chapter 29
Using the Distributed Transaction Manager

This chapter describes how to use the distributed transaction manager. It shows you how to use DECdtm services to bind together operations on several databases or files into a single transaction. To use DECdtm services, the resource managers taking part in the transaction must support DECdtm. DEC Rdb for OpenVMS Alpha, DEC Rdb for OpenVMS VAX, DEC DBMS for OpenVMS Alpha, DEC DBMS for OpenVMS VAX, and OpenVMS RMS Journaling support DECdtm.

This chapter is divided into the following sections:

Section 29.1 gives an introduction to DECdtm services.

Section 29.2 discusses how to call DECdtm services.

Section 29.3 gives an example that shows how to use DECdtm services.

29.1 Introduction to DECdtm Services

A transaction performs operations on resources. Examples of resources are databases and files. A transaction often needs to use more than one resource on one or more nodes. This type of transaction is called a distributed transaction.

Maintaining the integrity and consistency of the resources used by a distributed transaction can be complex. To help with this, DECdtm manages distributed transactions and reduces the amount of coding required in your applications.

DECdtm uses an optimized version of the standard two-phase commit protocol. This ensures that transactions are atomic. If a transaction is atomic, either all the transaction operations take effect (the transaction is committed), or none of the operations take effect (the transaction is aborted).

The two-phase commit protocol makes sure that all the operations can take effect before the transaction is committed. If any operation cannot take effect, for example if a network link is lost, then the transaction is aborted, and none of the operations take effect.

29.1.1 Sample Atomic Transaction

Edward Jessup, an employee of a computer company in Italy, is transferring to a subsidiary of the company in Japan. An application must remove his personal information from an Italian DBMS database and add them to a Japanese Rdb database. Both of these operations must happen, otherwise Edward may either end up "in limbo" (the application might remove him from the Italian database but then lose a network link while trying to add him to the Japanese database), or find that he is in both databases at the same time. Either way, the two databases would be out of step.

If the application used DECdtm to execute both operations as an atomic transaction, then this error could never happen; DECdtm would automatically detect the network link failure and abort the transaction. Neither of the databases would be updated, and the application could then try again.

29.1.2 Transaction Participants

A DECdtm transaction involves the following participants:

Figure 29-1 shows the participants in the distributed transaction discussed in Section 29.1.1. The application is on node ITALY.

Figure 29-1 Participants in a Distributed Transaction


29.1.3 DECdtm System Services

The DECdtm system services are:

These are all synchronous system service calls. There are also asynchronous versions (SYS$START_TRANS, SYS$END_TRANS, and SYS$ABORT_TRANS). For a full description of all the DECdtm system services, see the OpenVMS System Services Reference Manual.

29.1.4 Default Transactions

Some resource managers (such as OpenVMS RMS Journaling) support the concept of default transactions. This means that the application does not need to specify the transaction identifier when executing transaction operations. The resource manager checks whether the calling process has a default transaction; if it has, the resource manager assumes that the operation is part of the default transaction.

29.2 Calling DECdtm System Services

An application using the DECdtm system services follows these steps:

  1. Calls SYS$START_TRANSW. This starts a new transaction and returns the transaction identifier.
  2. Instructs the resource managers to perform the required operations on their resources.
  3. Ends the transaction in one of two ways:

29.3 Using DECdtm Services: An Example

The following is a sample Fortran application that uses DECdtm system services. It can be found in SYS$EXAMPLES:DECDTM$EXAMPLE1.

The application opens two files, sets a counter, then enters a loop to perform the following steps:

The application repeats these steps until either an error occurs or the user requests an interrupt. Because DECdtm services are used, the two files will always be in step with each other. If DECdtm services were not used, one file could have been updated while the other was not. This would result in the files' being out of step.

This example contains numbered callouts, which are explained after the program listing.


C 
C This program assumes that the files DECDTM$EXAMPLE1.FILE_1 and 
C DECDTM$EXAMPLE1.FILE_2 are created and marked for recovery unit 
C journaling using the command file SYS$EXAMPLES:DECDTM$EXAMPLE1.COM 
C 
C To run this example, enter the following: 
C   $ FORTRAN SYS$EXAMPLES:DECDTM$EXAMPLE1 
C   $ LINK DECDTM$EXAMPLE1 
C   $ @SYS$EXAMPLES:DECDTM$EXAMPLE1 
C   $ RUN DECDTM$EXAMPLE1 
C 
C SYS$EXAMPLES also contains an example C application, DECDTM$EXAMPLE2.C 
C The C application performs the same operations as this Fortran example. 
C  
        IMPLICIT    NONE 
 
        INCLUDE     '($SSDEF)' 
        INCLUDE     '($FORIOSDEF)' 
 
        CHARACTER*12 STRING 
        INTEGER*2   IOSB(4) 
        INTEGER*4   STATUS,COUNT,TID(4) 
        INTEGER*4   SYS$START_TRANSW,SYS$END_TRANSW,SYS$ABORT_TRANSW 
        EXTERNAL    SYS$START_TRANSW,SYS$END_TRANSW,SYS$ABORT_TRANSW 
        EXTERNAL    JOURNAL_OPEN 
C 
C Open the two files 
C 
(1)        OPEN (UNIT = 10, FILE = 'DECDTM$EXAMPLE1.FILE_1', STATUS = 'OLD', 
        1     ACCESS = 'DIRECT', RECL = 3, USEROPEN = JOURNAL_OPEN) 
        OPEN (UNIT = 11, FILE = 'DECDTM$EXAMPLE1.FILE_2', STATUS = 'OLD', 
        1     ACCESS = 'DIRECT', RECL = 3, USEROPEN = JOURNAL_OPEN) 
 
        COUNT = 0 
 
        TYPE *, 'Running DECdtm example program' 
        TYPE *, 'Press CTRL-Y to interrupt' 
C 
C Loop forever, updating both files under transaction control 
C 
        DO WHILE (.TRUE.) 
C 
C Update the count and convert it to ASCII 
C 
(2)          COUNT = COUNT + 1 
          ENCODE (12,8000,STRING) COUNT 
8000      FORMAT (I12) 
C 
C Start the transaction 
C 
(3)          STATUS = SYS$START_TRANSW (%VAL(1),,IOSB,,,TID) 
          IF (STATUS .NE. SS$_NORMAL .OR. IOSB(1) .NE. SS$_NORMAL) GO TO 9040 
C 
C Update the record in each file 
C 
(4)          WRITE (UNIT = 10, REC = 1, ERR = 9000, IOSTAT = STATUS) STRING 
          WRITE (UNIT = 11, REC = 1, ERR = 9010, IOSTAT = STATUS) STRING 
C 
C Attempt to commit the transaction 
C 
(5)          STATUS = SYS$END_TRANSW (%VAL(1),,IOSB,,,TID) 
          IF (STATUS .NE. SS$_NORMAL .OR. IOSB(1) .NE. SS$_NORMAL) GO TO 9050 
 
        END DO 
C 
C Errors that should cause the transaction to abort 
C 
(6)
9000    TYPE *, 'Failed to update DECDTM$EXAMPLE1.FILE_1' 
        GO TO 9020 
 
9010    TYPE *, 'Failed to update DECDTM$EXAMPLE1.FILE_2' 
9020    STATUS = SYS$ABORT_TRANSW (%VAL(1),,IOSB,,,TID) 
        IF (STATUS .NE. SS$_NORMAL .OR. IOSB(1) .NE. SS$_NORMAL) GO TO 9060 
        STOP 
C 
C Errors from DECdtm system services 
C 
9040    TYPE *, 'Unable to start a transaction' 
        GO TO 9070 
9050    TYPE *, 'Failed to commit the transaction' 
        GO TO 9070 
9060    TYPE *, 'Failed to abort the transaction' 
9070    TYPE *, 'Status = ', STATUS, ' IOSB = ', IOSB(1) 
        END 
C 
C Switch off TRUNCATE access and PUT with truncate on OPEN for RU Journaling 
C 
        INTEGER FUNCTION JOURNAL_OPEN (FAB, RAB, LUN) 
 
        INCLUDE '($FABDEF)' 
        INCLUDE '($RABDEF)' 
        INCLUDE '($SYSSRVNAM)' 
 
        RECORD  /FABDEF/ FAB, /RABDEF/ RAB 
 
        FAB.FAB$B_FAC = FAB.FAB$B_FAC .AND. .NOT. FAB$M_TRN 
        RAB.RAB$L_ROP = RAB.RAB$L_ROP .AND. .NOT. RAB$M_TPT 
 
        JOURNAL_OPEN = SYS$OPEN (FAB) 
        IF (.NOT. JOURNAL_OPEN) RETURN 
        JOURNAL_OPEN = SYS$CONNECT (RAB) 
 
        RETURN 
        END 
 


Previous Next Contents Index

  [Go to the documentation home page] [How to order documentation] [Help on this site] [How to contact us]  
  privacy and legal statement  
5841PRO_077.HTML