TweetFollow Us on Twitter

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 desirable to chain together a series of asynchronous routines. For example, you could open a file, write to the file, then close the file, all asynchronous. The completion of each of those operations could trigger the next.

There are two ways to find out if an asynchronous routine has completed. The most often used of which is a completion routine. There is a variable within the ParamBlockHeader called ioCompletion. This variable is a ProcPtr. If this variable is non-zero, it’s value will be used as an address and will be jumped to automatically when the asynchronous routine is completed.

At first glance, this appears to be the perfect solution to incorporating asynchronous routines into your program. Unfortunately, the completion routine may be executed at any time, including during interrupt time, or at a point in time when your application is not the current process your computer is handling. This means that you cannot call any routines which move memory. Well over half of the Macintosh Toolbox routines move memory, including most of the fundamental ones. This also means that, at the point your completion routine is called, your routine may not have access to your application’s global variables. There is a mechanism which allows you to gain access to your application’s global variables, but using it means you have to take yet another matter into consideration. You could potentially be modifying a global variable which another part of your code could be in the middle of using. This could be very hazardous unless you’ve written your code to take sudden changes in variables into consideration.

The second way to find out if an asynchronous routine has been completed is to poll the ioResult field of it’s parameter block. The ioResult field, like ioCompletion, is a variable within the ParamBlockHeader structure. While the asynchronous routine is executing, this variable has a positive value. When the asynchronous routine is completed, this variable holds an error code, which is a either a negative value, or zero (noErr).

Polling the ioResult field can be a lot more difficult than using a completion routine. In order to poll an ioResult field, you must have access to the parameter block. And, in order to really let the rest of your program continue to go about it’s business, you’d have to poll ioResult fields in your programs main loop, or at some other point in your code that gets executed repeatedly. In order to do this, you’d need access to all of your parameter blocks globally. And, if you did have access to all of them globally, you’d need a mechanism to find out which ones are being used, and which ones are idle. This is not very intuitive.

The best use for ioResult polling is for doing something in particular while you wait for your asynchronous routine to complete, not to make asynchronous routines transparent to the rest of your program. With ioResult polling, you could execute a routine asychronous, and use the little bit of time immediately after it’s started executing to update windows, etc, while you wait for it to complete. As far as the rest of your program is concerned, this might as well be considered sychronous execution.

For education purposes, we’re going to try to mix these two methods to develop a new means of handling asynchronous completion routines. A mechanism which we shall outline will allow you to provide a routine to be executed when the asynchronous routine is completed, yet will execute the routine during normal process time. This is done by polling the ioResult field until the asynchronous routine is complete, and then executing a specified completion routine.

This mechanism offers the best of both worlds, and the drawbacks of neither. You are not limited to non-memory moving routines, and you are able to specify an action be taken at the completion of an asynchronous routine. Because the completion routine is not executed at interrupt time, you’re free to deallocate the parameter block with DisposPtr, which would otherwise be off limits.

Our non-interrupt asynchronous completion routine mechanism will maintain a linked list of installed parameter blocks, periodically search this linked list for completed routines, and remove them from the list and execute their completion routines when completed.

First, let’s declare our linked list structure :


/* 1 */
typedef void (* CompletionProc)(ParmBlkPtr, long);

struct CompletionHandlerEntry {
 ParmBlkPtr pb;
 CompletionProc  doneProc;
 long   refCon;
 struct CompletionHandlerEntry** nextEntry;
};
typedef struct CompletionHandlerEntry CompletionHandlerEntry;

In Pascal, this would be :

/* 2 */
type
   CompletionHandlerEntryHand = ^CompletionHandlerEntryPtr;
   CompletionHandlerEntryPtr = ^CompletionHandlerEntry;
   CompletionHandlerEntry = record
      pb: ParmBlkPtr;
      doneProc: ProcPtr;
      refCon: longint;
      nextEntry: CompletionHandlerEntryHand;
   end;

Notice that in the C version of this structure we’ve declared a function pointer type. There is no equivalent to this in Pascal. We’ll discuss how the function is executed in Pascal later. Also, notice that in addition to storing the parameter block and completion routine, we are also storing a refCon variable in this structure. refCon is a commonly used name for a variable which the programmer can use for his or her own purposes. Because it’s a 4-byte value, you can allocate a pointer or a handle and safely store it in a refCon. This means that you can piggy-back any information you would like onto each parameter block in this completion routine mechanism.

Our program will have to maintain a linked list of these structures in order to implement this mechanism. The first entry in the list will have to be kept track of as a global variable, like so :

CompletionHandlerEntry **Asynchs;

In Pascal:

var
   Asynchs: CompletionHandlerEntryHand;

When the list is empty, this variable will be NULL (NIL, or zero). When there are entries in the list, this global variable will point the first entry. The list can be walked through by following the nextEntry pointer to the next entry in the list. The last entry in the list will have a nextEntry of NULL. At the start of your program you should make sure to initialize Asynchs to NULL.

Next we need to write two routines which act on these structures. One to allow you to add a parameter block to the list, and another to scan the list for completed routines. First, the routine to add the parameter block to the list :


/* 3 */
void InstallCompletion(ParmBlkPtr pb, CompletionProc theProc, 
 long refCon)
{
 CompletionHandlerEntry ** NewEntry;

 NewEntry = (CompletionHandlerEntry **)
 NewHandle(sizeof(CompletionHandlerEntry));
 if (NewEntry == NULL)
 {
 while (pb->ioParam.ioResult > 0)
 ;
 (*theProc)(pb, theProc);
 }
 else
 {
 (**NewEntry).pb = pb;
 (**NewEntry).doneProc = theProc;
 (**NewEntry).refCon = refCon;
 (**NewEntry).nextEntry = Asynchs;
 Asynchs = NewEntry;
 }
}

In Pascal :


/* 4 */
procedure InstallCompletion (pb: univ ParmBlkPtr;
   theProc: ProcPtr; refCon: longint);

var
   NewEntry: CompletionHandlerEntryHand;

begin
   Handle(NewEntry) := 
      NewHandle(sizeof(CompletionHandlerEntry));
   if NewEntry = nil then
      begin
         while pb^.ioResult > 0 do
            ;
         CallCompletion(pb, theProc);
      end
   else
      begin
         NewEntry^^.pb := pb;
         NewEntry^^.refCon := refCon;
         NewEntry^^.doneProc := theProc;
         NewEntry^^.NextOne := Asynchs;
         Asynchs := NewEntry;
      end;
end;


This routine will first allocate a new completion handler entry. If it fails, it will wait for the asynchronous routine to complete and then call the completion routine. This means the asynchronous routine will be performed pseudo-synchronously when memory is very low. If the allocation is successful, the completion handler entry is added to the beginning of our linked list.

There is room to expand upon this routine. It could be modified to use a default completion routine if a completion routine of NULL is passed to it. The default completion routine would probably only deallocate the parameter block. This routine could also be modified to save certain variables in the linked list structure to be restored when the completion routine is called. This could be very useful if you change your current resource file often. You could save the current resource fork when the asynchronous routine is installed, and restore it temporarily when the completion routine is called.

Next, we need a routine to scan this linked list for completed asynchronous routines, and execute their completion routines:


/* 4 */
void DoCompletions()
{
 CompletionHandlerEntry **curEntry = Asynchs;
 CompletionHandlerEntry **parentEntry = NULL;

 while (curEntry)
 {
 if ((**curEntry).pb->ioParam.ioResult <= 0)
 {
 if (parentEntry)
 (**parentEntry).nextEntry = (**curEntry).nextEntry;
 else
 Asynchs = (**curEntry).nextEntry;
 (*(**curEntry).doneProc) 
 ((**curEntry).pb, (**curEntry).refCon);
 DisposHandle((Handle)curEntry);
 curEntry = NULL;
 if (parentEntry)
 curEntry = (**parentEntry).nextEntry;
 else
 curEntry = Asynchs;
 }
 else
 {
 parentEntry = curEntry;
 curEntry = (**curEntry).nextEntry;
 }
 }
}

In Pascal :


/* 6 */
procedure CallDoneProc (pb: ParmBlkPtr; refCon: longint; 
   jmpAddr: ProcPtr);

inline
   $205F, $4E90;

procedure DoCompletions;

var
   curEntry: CompletionHandlerEntryHand;
   parentEntry: CompletionHandlerEntryHand;

begin
   curEntry = Asynchs;
   parentEntry = nil;
   while (curEntry <> nil) do
      begin
         if (curEntry^^.pb^.ioResult <= 0) then
            begin
               if (parentEntry <> nil) then
                  parentEntry^^.nextEntry :=
                     curEntry^^.nextEntry
               else
                  Asynchs := curEntry^^.nextEntry;
               CallDoneProc(curEntry^^.pb, curEntry^^.refCon, 
                  curEntry^^.doneProc);
               DisposHandle(Handle(curEntry));
               curEntry := nil;
               if (parentEntry <> nil) then
                  curEntry := parentEntry^^.nextEntry
               else
                  curEntry := Asynchs;
            end
         else
            begin
               parentEntry := curEntry;
               curEntry := curEntry^^.nextEntry;
            end;
      end;
end;

Notice the inline Pascal procedure. What it does is strip the last parameter off the stack, and jump to the address in memory that it represents with the rest of the parameters intact. Although this works, and is a commonly used way to execute ProcPtr’s in Pascal, the method we can use in C uses registers much more efficiently.

DoCompletions should be added to your event loop, or to some other point in your code which is executed repeatedly.

One important thing to note about this mechanism is that you cannot call InstallCompletion from within a completion routine. This is because of how the DoCompletion routine is organized. At the point your completion routine is being called, the linked list is being acted upon. Installing a new completion handler entry could cause the linked list to be incorrectly maintained. This can be fixed. Consider it an exercise to better familiarize yourself with the routines used in the mechanism. It would either be a matter of 1) keeping a list of completed asychronous routines, removing them from the linked list, then executing their completion routines, or 2) making DoCompletions execute in such a way that modifications to the linked list during the call to the completion routine are compensated for.

 
AAPL
$474.83
Apple Inc.
+7.47
MSFT
$32.39
Microsoft Corpora
-0.48
GOOG
$883.30
Google Inc.
-2.21

MacTech Search:
Community Search:

Software Updates via MacUpdate

TrailRunner 3.7.746 - Route planning for...
Note: While the software is classified as freeware, it is actually donationware. Please consider making a donation to help stimulate development. TrailRunner is the perfect companion for runners,... Read more
VueScan 9.2.23 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Acorn 4.1 - Bitmap image editor. (Demo)
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Mellel 3.2.3 - Powerful word processor w...
Mellel is the leading word processor for OS X, and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
Iridient Developer 2.2 - Powerful image...
Iridient Developer (was RAW Developer) is a powerful image conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more
Delicious Library 3.1.2 - Import, browse...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Epson Printer Drivers for OS X 2.15 - Fo...
Epson Printer Drivers includes the latest printing and scanning software for OS X 10.6, 10.7, and 10.8. Click here for a list of supported Epson printers and scanners.OS X 10.6 or laterDownload Now Read more
Freeway Pro 6.1.0 - Drag-and-drop Web de...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With it's user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Transmission 2.82 - Popular BitTorrent c...
Transmission is a fast, easy and free multi-platform BitTorrent client. Transmission sets initial preferences so things "Just Work", while advanced features like watch directories, bad peer blocking... Read more
Google Earth Web Plug-in 7.1.1.1888 - Em...
Google Earth Plug-in and its JavaScript API let you embed Google Earth, a true 3D digital globe, into your Web pages. Using the API you can draw markers and lines, drape images over the terrain, add... Read more

Guitar! by Smule Jams Out A Left-Handed...
Guitar! by Smule Jams Out A Left-Handed Mode, Unlocks All Guitars Posted by Andrew Stevens on August 13th, 2013 [ permalink ] | Read more »
KungFu Jumpu Review
KungFu Jumpu Review By Lee Hamlet on August 13th, 2013 Our Rating: :: FLYING KICKSUniversal App - Designed for iPhone and iPad Kungfu Jumpu is an innovative fighting game that uses slingshot mechanics rather than awkward on-screen... | Read more »
The D.E.C Provides Readers With An Inter...
The D.E.C Provides Readers With An Interactive Comic Book Platform Posted by Andrew Stevens on August 13th, 2013 [ permalink ] | Read more »
Choose ‘Toons: Choose Your Own Adventure...
As a huge fan of interactive fiction thanks to a childhood full of Fighting Fantasy and Choose Your Own Adventure books, it’s been a pretty exciting time on the App Store of late. Besides Tin Man Games’s steady conquering of all things Fighting... | Read more »
Terra Monsters Goes Monster Hunting, Off...
Terra Monsters Goes Monster Hunting, Offers 178 Monsters To Capture and Do Battle With Posted by Andrew Stevens on August 13th, 2013 [ permalink ] | Read more »
Blaster X HD Review
Blaster X HD Review By Jordan Minor on August 13th, 2013 Our Rating: :: OFF THE WALLiPad Only App - Designed for the iPad For a game set in a box, Blaster X HD does a lot of thinking outside of it.   | Read more »
Tube Map Live Lets You View Trains In Re...
Tube Map Live Lets You View Trains In Real-Time Posted by Andrew Stevens on August 13th, 2013 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Premier League Kicks Off This Week; Watc...
Premier League Kicks Off This Week; Watch Every Single Match Live Via NBC Sports Live Extra and Your iPhone or iPad Posted by Jeff Scott on August 13th, 2013 [ permalink ] | Read more »
Meet Daniel Singer, the Thirteen-Year-Ol...
Ever had the idea for an app, but felt like the lack of programming and design ability was a bit of a non-starter? Well, 13-year-old Daniel Singer has made an app. He’s the designer of Backdoor, a chat app that lets users chat with their friends... | Read more »
Flashout 2 Gets Revealed, Offers Up An E...
Flashout 2 Gets Revealed, Offers Up An Enhanced Career Mode and Exciting New Circuits Posted by Andrew Stevens on August 13th, 2013 [ permalink ] | Read more »

Price Scanner via MacPrices.net

Apple refurbished iPads and iPad minis availa...
 Apple has Certified Refurbished iPad 4s and iPad minis available for up to $140 off the cost of new iPads. Apple’s one-year warranty is included with each model, and shipping is free: - 64GB Wi-Fi... Read more
Snag an 11-inch MacBook Air for as low as $74...
 The Apple Store has Apple Certified Refurbished 2012 11″ MacBook Airs available starting at $749. An Apple one-year warranty is included with each model, and shipping is free: - 11″ 1.7GHz/64GB... Read more
15″ 2.3GHz MacBook Pro (refurbished) availabl...
 The Apple Store has Apple Certified Refurbished 15″ 2.3GHz MacBook Pros available for $1449 or $350 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. Read more
15″ 2.7GHz Retina MacBook Pro available with...
 Adorama has the 15″ 2.7GHz Retina MacBook Pro in stock for $2799 including a free 3-year AppleCare Protection Plan ($349 value), free copy of Parallels Desktop ($80 value), free shipping, plus NY/NJ... Read more
13″ 2.5GHz MacBook Pro on sale for $150 off M...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $1049.95 including free shipping. Their price is $150 off MSRP plus NY sales tax only. B&H will include free copies of Parallels Desktop... Read more
iPod touch (refurbished) available for up to...
The Apple Store is now offering a full line of Apple Certified Refurbished 2012 iPod touches for up to $70 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free: -... Read more
27″ Apple Display (refurbished) available for...
The Apple Store has Apple Certified Refurbished 27″ Thunderbolt Displays available for $799 including free shipping. That’s $200 off the cost of new models. Read more
Apple TV (refurbished) now available for only...
The Apple Store has Apple Certified Refurbished 2012 Apple TVs now available for $75 including free shipping. That’s $24 off the cost of new models. Apple’s one-year warranty is standard. Read more
AnandTech Reviews 2013 MacBook Air (11-inch)...
AnandTech is never the first out with Apple new product reviews, but I’m always interested in reading their detailed, in-depth analyses of Macs and iDevices. AnandTech’s Vivek Gowri bought and tried... Read more
iPad, Tab, Nexus, Surface, And Kindle Fire: W...
VentureBeat’s John Koetsier says: The iPad may have lost the tablet wars to an army of Android tabs, but its still first in peoples hearts. Second place, however, belongs to a somewhat unlikely... Read more

Jobs Board

Sales Representative - *Apple* Honda - Appl...
APPLE HONDA AUTOMOTIVE CAREER FAIR! NOW HIRING AUTO SALES REPS, AUTO SERVICE BDC REPS & AUTOMOTIVE BILLER! NO EXPERIENCE NEEDED! Apple Honda is offering YOU a Read more
*Apple* Developer Support Advisor - Portugue...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more than Read more
RBB - *Apple* OS X Platform Engineer - Barc...
RBB - Apple OS X Platform Engineer Ref 63198 Country USA…protected by law. Main Function | The engineering of Apple OS X based solutions, in line with customer and Read more
RBB - Core Software Engineer - Mac Platform (...
RBB - Core Software Engineer - Mac Platform ( Apple OS X) Ref 63199 Country USA City Dallas Business Area Global Technology Contract Type Permanent Estimated publish end Read more
*Apple* Desktop Analyst - Infinity Consultin...
Job Title: Apple Desktop Analyst Location: Yonkers, NY Job Type: Contract to hire Ref No: 13-02843 Date: 2013-07-30 Find other jobs in Yonkers Desktop Analyst The Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.