Mar 94 Top 10
Volume Number: 10
Issue Number: 3
Column Tag: Think Top 10
Non-interrupt 
Completion Routines
By Colen Garoutte-Carson, Symantec Technical Support, Symantec
Corp.
This is a monthly column written by Symantec’s Technical Support Engineers
intended to provide you with information on Symantec products. Each month we cover
either a specific application of tools or a “Q&A” list.
Non-interrupt Completion Routines
If you’re using high-level Macintosh toolbox routines to manipulate files,
devices, or drivers, chances are that you’re program could be executing much more
efficiently.
The most efficient way to do any file or driver access is asynchronously. When
you write to a file with the FSWrite routine, your application, as well as any
background process, is suspended while FSWrite waits for the disk to seek to the
appropriate sector. Upon reaching that sector, the drive controller issues an
interrupt, which prompts the CPU to start writing to the file. The time lost while
waiting for the disk to seek is time that could be better spent. When you write to a file
asychronously, control returns to your program immediately. The only time taken is
taken after the disk interrupt has been issued, at which time the operating system does
the appropriate work, and, often, waits for yet another interrupt to be issued. The
process of performing an asynchronous routine is entirely transparent to your
application.
All high-level file and device IO related Macintosh toolbox routines have
low-level counterparts. These routines begin with the letters “PB”, probably
because they are each passed a structure called a parameter block. Rather than passing
arguments necessary to complete an operation, fields of the parameter block are
assigned values, and a pointer to that parameter block is passed to the PB routine.
Parameter blocks are often used both to pass information to a PB routine, and return
information to the calling routine.
The second, and final argument to a PB routine is a Boolean, which is set to false
for synchronous, or true for asynchronous execution. All PB routines have only these
two arguments.
Different PB routines require different types, and different sizes of parameter
blocks. For example, the original set of Macintosh file IO routines, designed to be used
on the original flat volume architecture, require a structure called a ParamBlockRec.
In C and C++, a ParamBlockRec is actually a union of a number of other parameter
blocks types. In Pascal, a cased record is used. Different PB routines require
variables from different union members. PBRead and PBWrite require an IOParam
parameter block. PBCreate and PBDelete require a FileParam parameter block. You
can either use a ParamBlockRec structure for calling all of these routines, or the exact
structure a particular routine requires. You can use the same ParamBlockRec for
many different consecutive PB routines because it’s allocated to the size of the largest
member of the union. But, if you use an IOParam, you will not be able to use it with
any PB routines other than those which accept IOParam’s. For more information on
the organization of the many types of Parameter Blocks, take a look at Inside Macintosh
: Files, Inside Macintosh : Devices, or the Think Reference.
All parameter blocks begin with the same basic structure, a ParamBlockHeader.
The first two fields of a ParamBlockHeader, qLink and qType, are also fields of a QElem
structure. QElem structures are queue element structures used internally by a
driver. QElem structures and parameter blocks are interchangeable. The QElem
structures that a driver is using at any given moment are probably parameter blocks,
passed to it by a PB routine.
Even if you are not using asynchronous IO, calling the low level PB routines
instead of their high level counterparts can save time. High level routines in turn call
PB routines. Filling out the parameter blocks and calling the PB routines yourself
takes more code space, but saves a little bit of CPU time. Also, sometimes there can be
more flexibility built into the PB version of a routine.
There are few functional differences between using PB routines synchronously
and using the high level routines, but calling PB routines asynchronously adds some
new considerations to your coding. The parameter block structure you pass to a PB
routines must remain valid, and not move in memory until the PB routine is
completed. This means that you cannot pass a pointer to a parameter block declared
locally within your function if your function may be exited before the routine is
completed. This also means that you cannot reuse a parameter block until the PB
routine you’ve used it in last is completed. Attempting to reuse a parameter block,
which is currently being used by another PB routine, would modify information which
may still be in use. This could cause unpredictable results.
Once you know an asynchronous routine has completed, you can use the
information it returns, if any, and reuse or deallocate the parameter block. Often it’s