|
OpenVMS Programming Concepts Manual
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:
- STATUS ='NEW'
- ACCESS = 'SEQUENTIAL'
- ORGANIZATION = 'SEQUENTIAL'
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:
- FAB address---Declare this argument as a RECORD variable. Use the
record structure FABDEF defined in the $FABDEF module of
SYS$LIBRARY:FORSYSDEF.TLB.
- RAB address---Declare this argument as a RECORD variable. Use the
record structure RABDEF defined in the $RABDEF module of
SYS$LIBRARY:FORSYSDEF.TLB.
- Logical unit number---Declare this argument as an INTEGER.
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.
- Opens the file---To open the file, invoke the SYS$OPEN system
service if the file already exists, or the SYS$CREATE system service if
the file is being created.
- Connects the file---Invoke the SYS$CONNECT system service to
establish a record stream for I/O.
- Returns the status---To return the status, equate the return status
of the SYS$OPEN or SYS$CREATE system service to the function value of
the user-open routine.
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:
- Include the modules in your program unit.
- Declare RECORD variables for the FAB and the RAB.
- 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:
- Application: Defines the operations that the
transaction will perform.
- Resource manager: Performs the operations on the
resources. A resource manager must support DECdtm. Examples of those
that do are Rdb, DBMS, and OpenVMS RMS Journaling.
- Transaction manager: Coordinates the actions of
the resource managers on its node. Transaction managers are provided by
DECdtm.
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:
- SYS$START_TRANSW: Starts a new transaction and returns the
transaction identifier
- SYS$END_TRANSW: Ends a transaction by attempting to commit it;
returns the outcome of the transaction (either commit or abort)
- SYS$ABORT_TRANSW: Aborts a transaction
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:
- Calls SYS$START_TRANSW. This starts a new transaction and returns
the transaction identifier.
- Instructs the resource managers to perform the required operations
on their resources.
- Ends the transaction in one of two ways:
- Commit: To attempt to perform, or commit, the
transaction, the application calls SYS$END_TRANSW. This checks whether
all the participants can commit their operations. If any participant
cannot commit an operation, the transaction is aborted.
When
SYS$END_TRANSW returns, the application finds out the outcome of the
transaction by reading the completion status in the I/O status block.
- Abort: To abort the transaction, the application
calls SYS$ABORT_TRANSW. Typically, an application aborts a transaction
if a resource manager returns an error or if the user enters invalid
information during the transaction.
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:
- Increments the counter by 1
- Calls SYS$START_TRANSW to start a new transaction
- Writes the counter value to the two files
- Either calls SYS$END_TRANSW to attempt to commit the transaction,
or calls SYS$ABORT_TRANSW to abort the transaction
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
|
|