    I want to get Macintosh programmers up in arms about the shameful state
of 3rd party Macintosh hard disk driver software. The Mac operating system
supports a wonderful feature that could let our programs run much faster,
but the developers of disk driver software have cheated us out of it.

    I am referring to asynchronous disk i/o. According to Inside Mac, if you do
a PBRead(&ioBlock, TRUE), or, a PBWrite(&ioBlock,  TRUE), the call
should schedule the i/o, and return almost immediately, and when the i/o
is actually done, the system will set some fields in the ioBlock, and call the
completion routine associated with the ioBlock.

    This is useful because the disk i/o is mostly waiting for the disk heads to
seek to the right track. That waiting time could be spent doing useful
processing (particularly under a future version of multifinder, which
could give that time to another task.) That is the traditional use  of disk i/o
time in a multi-tasking operating system. 

In addition, there are special uses of asynchronous i/o that only make sense in
a personal system.  In a multi-user system,  your programs makes a disk request,
and waits, meanwhile the o.s. runs somebody else's program. This is known as
"demand page faulting" and is the worst possible way to organize a personal
computer system, because often you only care about _one_ program being fast:
the one you are interacting with right now. When that program needs something
from disk, it doesn't tell the O.S. until it actually needs it, and it has to 
twiddle its thumbs while the O.S. fetches it.

I've got papers in my files from 10 years ago on compiling number-crunching 
programs  for personal computers to pre-fetch disk based arrays so that the 
program _never_  has to wait for the disk: the pieces of the arrays it needs
are already in memory by the time it needs them. This is known as 
"pre-fetching", and judicious use of it lets a 1 Megabyte RAM machine with a
40 Meg disk run 99% as fast a 40 Meg RAM machine, when crunching 40Meg
worth of arrays.

The Macintosh has a chance to show clear superiority over other brands here.
MSDOS does not support asynchronous disk i/o. Unix does not support
asynchronous disk i/o. Don't you think Apple would love to have 
number-crunching programs that run fine on a Macintosh, but run 
100 times slower for problems of the same size when run on a 386 or a Sun?

But, most third party developers of hard disks simply didn't implement
asynchronous disk i/o. The hard disk makers cheat the public by leaving this
feature out, and we software developers are left without a valuable tool.

Apple's floppy disk drivers support asynchronous i/o. The simple test program
included here shows that for this example, more than 99% of the time to do a
disk read happens between the end of the scheduling part of the PBRead, and the
end of the actual data transfer.  Now admitedly some of that time is spent
servicing the drive, but much of that time could be spent getting work done.

Let us create a hall of shame. Does your hard disk support asynchronous i/o?
if not, please post their name here, so we can complain. So we can get some
action, so we can get disk based programs that run at all-RAM speeds!

(I compiled the following program with LightSpeed C 3.0, 
linked with MacTraps and stdio.)

----------- cut here --------------------- cut here -------------------------
/* Asynch i/o - program to report if your hard drive supports asynch disk i/o.
 */
#include <stdio.h>
#include <OSUtil.h>
#include <FileMgr.h>

char *header =     "Test of Asynchronous Disk I/O, Version 1.0,\n\
Copyright © 1988, by David Phillip Oster\n\
Mosaic Codes, Suite 2036, 2140 Shattuck Ave., Berkeley CA 94704, USA\n\n\
You may copy this program freely, except that Think, Inc.\n\
retains copyright to libraries used by this program.\n\n\
Warning: times reported by this program are only accurate to the \n\
nearest 0.02 second\n\n\
Start Test: reading 100k from the System File.\n\n";

char *congrat1 = "Congratulations: your drive supports asynchronous i/o.\n";
char *congrat2 = 
   "Your drive would have let you use %.1f%% of otherwise wasted time.\n";

unsigned long readComplete = 0;    /* time read actually finshes trasnfering data */
Ptr     ioBuf;                     /* buffer for access path */
IOParam io;                        /* io parameter block for all our disk i/o */
int ioCnt = 0;                     /* count of i/o calls, for error checking */

char *ioName[] = {
    "SysEnvirons",
    "PBOpenRF",
    "PBGetEOF",
    "PBRead",
    "AwaitFinish"
};

/* Completion -
 */
pascal void Completion(){
    SetUpA5();
    readComplete = TickCount();
    RestoreA5();
}

/* AwaitFinish -
 */
OSErr AwaitFinish(){
    unsigned long start;

    start = TickCount();
    while(io.ioResult > 0){
        /* we do nothing in this toy program. In a real program, */
        /* we would do useful work here */
        if( (unsigned long) TickCount() - start > 1500L){
            printf("TOO BAD: read time out, ioResult =%d\n", io.ioResult);
            break;
        }
    }
    return noErr;
}

/* macro to propagate i/o errors 
 */
#define IO(expr)if(noErr == val){val = expr;ioCnt++;}

main(){
    unsigned long start, readReturned;
    long siz;
    OSErr val, oVal;
    SysEnvRec world;
    Ptr buffer;

    printf(header);
    SysEnvirons(1, &world);
    val = noErr;
    if(noErr == val){ioBuf = NewPtr(522L); val = MemError(); }

    io.ioCompletion = NULL;
    io.ioNamePtr    = (StringPtr) "\pSystem";
    io.ioVRefNum    = world.sysVRefNum;
    io.ioVersNum    = 0;
    io.ioPermssn    = fsRdPerm;
    io.ioMisc       = ioBuf;    /* get speed: give this file its own buffer */

            /* file manager can't take asynch calls */
    IO(oVal = PBOpenRF(&io, FALSE));
    IO(PBGetEOF(&io,FALSE));
    siz = (long) io.ioMisc;
    if(siz < 102400L){
        siz = 102400L;
    }
    if(noErr == val){buffer = NewPtr(siz); val = MemError(); }

    start = TickCount();

    io.ioCompletion = (ProcPtr) Completion;
    io.ioBuffer     = buffer;
    io.ioReqCount   = siz;
    io.ioPosMode    = fsAtMark;
            /* disk manager asynch call */
    IO(PBRead(&io, TRUE));
    readReturned = TickCount();
    IO(AwaitFinish());
    if(noErr == oVal){    val = PBClose(&io, FALSE);    }
    if(readReturned == start){    /* time was too small to measure accurately */
        start--;
    }
    if(noErr == val){
        printf("Read Returned after %.2f seconds\n", (readReturned-start)/60.);
        if(0 == readComplete){
            printf("TOO BAD: Completion routine never called.\n");
        }else if(readComplete < readReturned){
            printf("TOO BAD: Completion routine called before Read returned.\n");
        }else{
            printf("Read complete after %.2f seconds\n", 
                (readComplete-start)/60.);
            if(readComplete <= readReturned){
                printf("TOO BAD: no asynchronous i/o here.\n");
            }else{
                printf(congrat1);
                printf(congrat2, 
                    (readComplete-readReturned)*100./(readComplete-start));
            }
        }
    }else{
        printf("I/O Error Occurred, %s, Error = %d", ioName[ioCnt], val);
    }
}

