home *** CD-ROM | disk | FTP | other *** search
- *******************************************************************************
-
- FLUSH.ACC
- Version 1.3
- February 10, 1986
- by Randy Forgaard
- CompuServe 70307,521
-
-
- This file, FLUSH.ACC, is related to the file FLUSH.PAS in DL 1 of the Borland
- SIG on CompuServe, but FLUSH.PAS is not needed in order to use the routines
- below.
-
- This file is for use with the Turbo Access portion of the MS-DOS Turbo
- (Database) Toolbox Version 1.2, together with MS-DOS or PC-DOS Turbo Pascal
- 3.01A or higher (regular, 8087, or BCD version). If you have Version 1.00,
- 1.01, or 1.1 of the Toolbox, see the file TBXFIX in DL 1 of the Borland SIG for
- the source code changes that will upgrade your copy to Version 1.2 (plus
- additional fixes). If you have Version 3.00A or 3.00B of Turbo Pascal, arrange
- with Borland Customer Service to upgrade your copy of Turbo to 3.01A or higher
- (3.00A and 3.00B are not compatible with Turbo Database Toolbox). The routines
- below will not work with earlier (pre-3.0) versions of Turbo Pascal, nor with
- Turbo Pascal for operating systems other than DOS.
-
- This file describes certain changes that you can make to the source code of
- Turbo Access to make your database virtually impervious to system crashes.
- Normally, when you use Turbo Access and you use MakeFile, AddRec, PutRec,
- DeleteRec, MakeIndex, AddKey, or DeleteKey to create or change a DataFile or
- IndexFile, not all of the changes you make are necessarily written to the disk
- right away. Typically, the most recent changes are kept in memory buffers by
- Turbo Access, the Turbo run-time system, and/or DOS. This technique allows
- file output to proceed more quickly, since an entire index page or disk sector
- can be written to the disk at once. When a file gets closed, its memory
- buffers are all written to disk. However, if the system should crash (due to
- program error, accidental Ctrl-Alt-Del, power failure, tripping over the plug,
- etc.) after a file has been changed and before it gets closed, the recent
- changes that are in memory but not on the disk will not get written. Even more
- insidious, a system crash can mean that the file length in the directory entry,
- or the record numbers in IndexFiles, do not get written to disk, rendering the
- information in the database inconsistent and probably unusable.
-
- One can guard against system crashes by flushing all information in a file's
- memory buffers out to disk, every time that the contents of that file changes.
- Unfortunately, using the routines provided by Turbo Access, a DataFile or
- IndexFile can only be flushed by closing and reopening the file. This is
- primarily because DOS provides no explicitly documented way to flush a file's
- buffers without closing/reopening. Closing and reopening a file every time its
- contents change is extraordinarily expensive at run-time, largely because DOS
- must perform a directory lookup every time a file is reopened.
-
- Fortunately, there actually IS a little-known method for flushing a file's
- buffers and updating the file length in the file's directory entry under DOS,
- without reopening the file. The technique for "flushing" a file is: 1) invoke
- DOS function 45H, "Duplicate a File Handle (DUP)," to duplicate the file
- handle, and then 2) invoke DOS function 3EH, "Close a File Handle" to close the
- extra file handle that you just created (this action does not close the
- original file handle). The "Close" function 3EH flushes the file's buffers, as
- documented in the DOS Technical Reference manual. Yet the original file handle
- is still valid and usable, so the file does not need to be reopened. Thanks
- for this clever technique go to Dan Daetwyler in a letter to Ray Duncan's
- 16-Bit Software Toolbox column in the December '85 issue of Dr. Dobb's Journal.
-
- One of the routines below is a new routine for use with Turbo Access, called
- FlushFile. This procedure will perform an actual flush on any DataFile, so
- that the DataFile will be completely up-to-date and consistent even if the
- system should crash after the flush. The FlushFile routine uses the technique
- described above. Note: The FLUSH.PAS file, mentioned above, contains two
- routines for flushing standard Turbo files (rather than Turbo Access files).
- The FlushAny procedure in FLUSH.PAS is very similar to the FlushFile procedure
- below. The differences are that FlushFile updates the internal Record 0
- maintained by Turbo Access, and that FlushFile uses TaIOcheck, the internal
- Turbo Access routine, to report errors, rather than writing the errors directly
- to the primary output.
-
- Just as FlushFile will flush any DataFile, FlushIndex (the other procedure
- below) will flush any IndexFile. The FlushIndex code is similar to the
- CloseIndex routine included with Turbo Access, with the following differences:
- the index pages are not removed from the page stack after they are written to
- disk (which can make subsequent index searches proceed faster), and the final
- call to CloseFile is replaced by a call to FlushFile.
-
- After making the Turbo Access source code changes indicated below, FlushFile
- and FlushIndex may be called explicitly by the calling program whenever
- desired. Alternatively, the calling program may set the global Boolean
- variable FailSafe to "true," and the (modified) Turbo Access routines will
- automatically invoke FlushFile or FlushIndex (depending on context) whenever a
- DataFile or IndexFile is changed. By default, FailSafe is "false." If you
- decide to set FailSafe to "true," it should be done immediately after the call
- to InitIndex at the beginning of your main program; i.e.:
-
- ...
- InitIndex;
- FailSafe := true;
- ...
-
- With the changes below, the only possibility that data can become corrupted due
- to a system crash is if the crash occurs between when the file gets changed and
- when FlushFile or FlushIndex is automatically (or explicitly) invoked
- immediately afterward. That window of vulnerability is a small number of
- microseconds, and occurs only at the actual moment that the program outputs a
- new data record or index key. If your program is reading records, searching
- the database, waiting for user input, computing, printing, updating the screen,
- etc., your database is still safe if the system crashes. However, if there
- should be a power failure at the exact moment that data is being written, it is
- likely that the failure will also cause serious problems on the disk as a
- whole, trashing the FAT or some directories. Thus, in this very unlikely
- event, the disk may need to be restored from backup tape or disks in any case,
- and you probably shouldn't lose sleep over this possibility.
-
- There are two important DISadvantages to using the Turbo Access code changes
- below, assuming you have set FailSafe to "true":
-
- 1) MakeFile, AddRec, PutRec, DeleteRec, MakeIndex, AddKey, and DeleteKey
- will all execute somewhat slower, because of the automatic file flush. This
- delay will not be nearly as great as closing/reopening the file, but it can be
- noticeable if done frequently. If your database application spends a
- significant amount of time updating records, you might want to test how much
- slower your application runs with FailSafe = "true" than with FailSafe =
- "false." You can reduce the delay by living a little dangerously and running
- with FailSafe = "false," invoking FlushFile and FlushIndex explicitly at
- strategic points.
-
- 2) The file flushing code below only works with DOS. If you plan to port
- your application to other operating systems later, you may want to build a more
- robust scheme for detecting and recovering from system crashes, since you won't
- be able take advantage of the crash protection below.
-
- It would be wonderful if Turbo Access could be made to automatically flush file
- buffers under other operating systems, in addition to DOS. If you find a way
- to do this, PLEASE PLEASE add this information to the FLUSH.ACC file
- accordingly, and re-upload to DL 1 of the Borland SIG! For that matter, if you
- are feeling truly altruistic, you might want to make similar additions to
- FLUSH.PAS, too, showing how to flush normal Turbo files under other operating
- systems.
-
- There are some other files in DL 1 of the Borland SIG that may be of interest.
- These files describe how to modify the Database Toolbox to extend it for
- various other purposes; e.g., EXTEND.ACC describes Toolbox modifications that
- allow one to open up to 96 Toolbox files simultaneously (252 under DOS 3.0 and
- higher) under DOS Turbo 3.0, and BIGTRE.BOX shows how to use more than 65535
- records per DataFile under DOS. The modifications below have been tested with
- EXTEND.ACC, so you may use the FLUSH.ACC and EXTEND.ACC modifications together.
- However, the BIGTRE.BOX modifications have not yet been examined with respect
- to FLUSH.ACC and EXTEND.ACC. Try a "BRO/KEY:TOOLBOX" and a "BRO/KEY:ACCESS" to
- find these and other files in DL 1.
-
- Note: If you are using both the FLUSH.ACC modifications and the EXTEND.ACC
- modifications, you must apply the FLUSH.ACC modifications (below) first, THEN
- the EXTEND.ACC modifications.
-
- Many thinks to Rick Amerson (CompuServe 72477,1566) for his generous help in
- testing the code below, to Andy Miller (CompuServe 70357,3656) for pointing out
- that the routines below do not use any undocumented features of DOS function
- calls, and to Peter Thomas (CompuServe 75716,2377) for suggesting the
- efficiency improvement to FlushIndex.
-
- Change Log:
-
- Version 1.1: Removed caveats about undocumented use of DOS functions, since
- this file actually only uses documented features of DOS.
-
- Version 1.2: Slight changes to FlushIndex so that index pages do not get
- removed from the page stack in memory when they get flushed to
- disk. Can make subsequent index searches faster, since those
- index pages will not need to be re-read from disk.
-
- Version 1.3: Updated to reflect the changes in Version 1.2 of the Database
- Toolbox, and the new version of TBXFIX.
-
- *******************************************************************************
-
-
- The code changes to add "flush" capability to MS-DOS Turbo Access Version 1.2
- are shown below. Please BE SURE to make a backup copy of your Turbo Access
- source code before making these changes. Note: When making the changes below,
- be sure to use the version of ACCESS.BOX intended for use with Turbo 3.0. This
- file might be called ACCESS.BOX or ACCESS3.BOX on your Toolbox disk. Check the
- comment header at the beginning of the file to ensure you are using the correct
- ACCESS.BOX.
-
-
-
- STEP 1: Add the following declaration to ACCESS.BOX, immediately prior to
- TaIOcheck:
-
- const
- FailSafe: Boolean = false;
-
-
-
- STEP 2: Insert the following routine immediately after the routine TaIOcheck in
- ACCESS.BOX:
-
-
- {Flushes the buffers associated with the DataFile "DatF," and updates the file
- length in the directory entry of "DatF," without closing "DatF."}
-
- procedure FlushFile (var DatF: DataFile);
- var
- handle: Integer absolute DatF; {File handle is the first word of a DataFile}
- regs: record
- case Integer of
- 1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Integer);
- 2: (AL, AH, BL, BH, CL, CH, DL, DH: Byte)
- end;
- begin
- {Code from beginning of CloseFile}
- DatF.Int2 := DatF.NumRec;
- Move(DatF.FirstFree, TaRecBuf, 8);
-
- {Simulate a PutRec(DatF, 0, TaRecBuf) (to avoid FlushFile/PutRec recursion)}
- Seek(DatF.F, 0);
- IOstatus := IOresult;
- TaIOcheck(DatF, 0);
- BlockWrite(DatF.F, TaRecBuf, 1);
- IOstatus := IOresult;
- TaIOcheck(DatF, 0);
-
- {Flush DatF}
- IOstatus := $F0; {"Disk write error" I/O error...just in case}
- regs.AH := $45; {DOS function to duplicate a file handle}
- regs.BX := handle;
- MsDos(regs);
- if Odd(regs.Flags) then {Check if carry flag is set}
- TaIOcheck(DatF, 0);
- regs.BX := regs.AX; {Put new file handle into BX}
- regs.AH := $3E; {Dos function to close a file handle}
- MsDos(regs);
- if Odd(regs.Flags) then {Check if carry flag is set}
- TaIOcheck(DatF, 0)
- end {FlushFile};
-
-
-
- STEP 3: Insert the following routine immediately after the routine OpenIndex in
- ACCESS.BOX:
-
-
- {Flushes the buffers associated with the IndexFile "IdxF," and updates the file
- length in the directory entry of "IdxF," without closing "IdxF."}
-
- procedure FlushIndex (var IdxF: IndexFile);
- var
- I: Integer;
- begin
- {Similar to CloseIndex:}
- for I := 1 to PageStackSize do
- with TaPageStk[I] do
- if (IndexFPtr = Addr(IdxF)) and Updated then
- begin
- TaPack(Page,IdxF.KeyL);
-
- {Simulate a PutRec(IdxF.DataF, PageRef, Page) (to avoid redundant
- FlushFile calls)}
- Seek(IdxF.DataF.F, PageRef);
- IOstatus := IOresult;
- TaIOcheck(IdxF.DataF, PageRef);
- BlockWrite(IdxF.DataF.F, Page, 1);
- IOstatus := IOresult;
- TaIOcheck(IdxF.DataF, PageRef);
-
- TaUnpack(Page,IdxF.KeyL);
- Updated := false
- end;
- IdxF.DataF.Int1 := IdxF.RR;
- FlushFile(IdxF.DataF)
- end {FlushIndex};
-
-
-
- STEP 4: Also in ACCESS.BOX, add the following line just before the "end" at the
- end of the PutRec routine:
-
- if FailSafe then FlushFile(DatF);
-
-
-
- STEP 5: Insert the following line just before the very last "end" statement at
- the very end of the file ADDKEY.BOX:
-
- if FailSafe then FlushIndex(IdxF);
-
-
-
- STEP 6: Also, insert the above line just before the very last "end" statement
- at the very end of the file DELKEY.BOX.
-
-
-
- STEP 7: Don't forget to add the line "FailSafe := true;", if desired, to your
- main program, immediately after the call to InitIndex.
-
-
-
- That's it! If you have any questions, comments, or trouble with the above
- changes, please feel free to contact me on the Borland SIG or via EasyPlex on
- CompuServe. I sincerely hope the above information is useful to you.
-
- -- Randy Forgaard