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
$106.98
Apple Inc.
-0.36
MSFT
$46.05
Microsoft Corpora
-0.57
GOOG
$550.31
Google Inc.
+0.98

MacTech Search:
Community Search:

Software Updates via MacUpdate

Cocktail 8.0.1 - General maintenance and...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
LibreOffice 4.3.3.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
VMware Fusion 7.0.1 - Run Windows apps a...
VMware Fusion allows you to create a Virtual Machine on your Mac and run Windows (including Windows 8.1) and Windows software on your Mac. Run your favorite Windows applications alongside Mac... Read more
OneNote 15.3.2 - Free digital notebook f...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more
Audio Hijack Pro 2.11.4 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Iridient Developer 3.0.0 beta 3 - Powerf...
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
TextWrangler 4.5.11 - Free general purpo...
TextWrangler is the powerful general purpose text editor, and Unix and server administrator's tool. Oh, and also, like the best things in life, it's free. TextWrangler is the "little brother" to... Read more
NeoFinder 6.6 - Catalog your external me...
NeoFinder (formerly CDFinder) rapidly organizes your data, either on external or internal disks, or any other volumes. It catalogs all your data, so you stay in control of your data archive or disk... Read more
Chromium 38.0.2125.111 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
Default Folder X 4.6.11 - Enhances Open...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click... Read more

Latest Forum Discussions

See All

Audio Defence : Zombie Arena (Games)
Audio Defence : Zombie Arena 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: A zombie shooter audio game. Made from gut-wrenching 3D binaural sound, for a new kind of weird immersion. You... | Read more »
RPG Asdivine Hearts (Games)
RPG Asdivine Hearts 1.1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.1.0 (iTunes) Description: SPECIAL PRICE50% OFF (USD 7.99 -> USD 3.99)!!! Travel alongside four companions and a cat in the adventure of a... | Read more »
Haunt the House: Terrortown (Games)
Haunt the House: Terrortown 1.0.1 Device: iOS Universal Category: Games Price: $.99, Version: 1.0.1 (iTunes) Description: 66.6% OFF! SPECIAL SPOOKY HALLOWEEN LAUNCH PRICE! 66.6% OFF! ...What was that sound? Is somebody there? | Read more »
SAS: Zombie Assault 4 Review
SAS: Zombie Assault 4 Review By Jennifer Allen on October 30th, 2014 Our Rating: :: FLAWED SHOOTERUniversal App - Designed for iPhone and iPad Shoot everything that moves in this fun, if flawed, twin-stick shooter.   | Read more »
Naailde the Witch Review
Naailde the Witch Review By Amy Solomon on October 30th, 2014 Our Rating: :: PITCH-PERFECT STORYTELLINGUniversal App - Designed for iPhone and iPad Marvelous storytelling, narration, and moving illustrations make this storybook... | Read more »
1st & Goal Review
1st & Goal Review By Andrew Fisher on October 30th, 2014 Our Rating: :: FOR THE D&D LOVING QBUniversal App - Designed for iPhone and iPad 1st & Goal is a board gamer’s football game, a football fan’s board game, and... | Read more »
French Developer Pated Unveils Seashine
French Developer Pated Unveils Seashine Posted by Ellis Spice on October 30th, 2014 [ permalink ] French one-man studio Pated has unveiled Seashine, “a poetic journey into the abyss.” Players take on the role of a jellyfish strugglin | Read more »
Agents of Storm: Tips, Tricks, and Strat...
Calling all agents: Would you like to see what we thought of this rather pretty base builder? Check out our Agents of Storm review! Have you downloaded Agents of Storm, been bowled over by the graphics, and aren’t quite sure what to do next? Never... | Read more »
Any.DO 2.0 Hopes to Help Manage Producti...
Any.DO 2.0 Hopes to Help Manage Productivity Posted by Ellis Spice on October 30th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
Base Busters Review
Base Busters Review By Jennifer Allen on October 30th, 2014 Our Rating: :: FUN BUT RESTRICTED MIXUniversal App - Designed for iPhone and iPad Mixing up two forms of tower defense gaming and collectible cards, Base Busters is a fun... | Read more »

Price Scanner via MacPrices.net

Apple Regains Momentum As Windows Stutters An...
The latest smartphone sales data from Kantar Worldpanel ComTech, for the three months to March 2014, shows Apple performing strongly in the first quarter of the year, with sales bouncing back in... Read more
Worldwide Smartphone Shipments Increase 25.2%...
New smartphone releases and an increased emphasis on emerging markets drove global smartphone shipments above 300 million units for the second consecutive quarter, according to preliminary data from... Read more
Apple now offering refurbished 2014 15-inch M...
The Apple Store is now offering Apple Certified Refurbished 2014 15″ Retina MacBook Pros for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
Apple drops prices on refurbished 2013 Retina...
The Apple Store has dropped prices on 2013 Apple Certified Refurbished 13″ and 15″ Retina MacBook Pros, with Retina models now available starting at $999. Apple’s one-year warranty is standard, and... Read more
New 2.8GHz Mac mini on sale for $949, save $5...
Abt Electronics has the new 2.8GHz Mac mini in stock and on sale for $949.05 including free shipping. Their price is $50 off MSRP, and it’s the lowest price available for this model from any reseller... Read more
Sale! 3.7GHz Quad Core Mac Pro available for...
 B&H Photo has the 3.7GHz Quad Core Mac Pro on sale for $2649 including free shipping plus NY sales tax only. Their price is $350 off MSRP, and it’s the lowest price for this model from any... Read more
Mujjo Steps Up The Game With Refined Touchscr...
Netherlands based Mujjo have just launched their Refined Touchscreen Gloves, stepping up their game. The gloves feature a updated elegant design that takes these knitted gloves to the next level. A... Read more
Sale! Preorder the new 27-inch 5K iMac for $2...
 Abt Electronics has the new 27″ 3.5GHz 5K iMac on sale and available for preorder for $2374.05 including free shipping. Their price is $125 off MSRP, and it’s the lowest price available for this... Read more
Simplex Solutions Inc. Brings Secure Web Surf...
New York based Simplex Solutions Inc. has announced the release and immediate availability of Private Browser 1.0, its revolutionary new secure web browser developed for iPhone, iPad and iPod touch... Read more
Save up to $180 off MSRP with an Apple refurb...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free.... Read more

Jobs Board

Position Opening at *Apple* - Apple (United...
**Job Summary** Every day, business customers come to the Apple Store to discover what powerful, easy-to-use Apple products can do for them. As a Business Leader, Read more
Sr. Manager, *Apple* Deployment Programs fo...
**Job Summary** Apple is seeking candidates for a new position on the Education Content and Technology team. iPad and Mac is in the hands of millions of teachers and Read more
*Apple* Solutions Consultant (ASC) - Apple I...
…important role that the ASC serves is that of providing an excellent Apple Customer Experience. Responsibilities include: * Promoting Apple products and solutions Read more
*Apple* Solutions Consultant (ASC) - Apple I...
…important role that the ASC serves is that of providing an excellent Apple Customer Experience. Responsibilities include: * Promoting Apple products and solutions Read more
*Apple* Solutions Consultant (ASC) - Apple I...
…important role that the ASC serves is that of providing an excellent Apple Customer Experience. Responsibilities include: * Promoting Apple products and solutions Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.