From: ejohnson@void.ncsa.uiuc.edu (Eric E. Johnson)
Subject: MacTCP, ASR's and completion routine hell
Organization: University of Illinois at Urbana
Date: Thu, 1 Oct 1992 17:01:18 GMT

I've been working on a networked, role playing game.  So those who can
help will be helping to alleviate the relative dearth of games on the
Macintosh.

The game requires that there be frequent exchanges of data between the
server and client.  The amount of data is small, under 100 bytes, and
I've been referring to the exchanges as the heartbeat.  As an example,
the heartbeat leaves the server, and is sent to the client.  The
client reads it in, processes the client's data, and sends it back to
the server.

The idea is to have the rate of exchange as high as possible.  Since
we are using a Unix based server, turn around time for the heartbeat
on the server end is fairly fast.  Our problem has been the Mac end.

After some initial work on the game, I realized that using null events
from WaitNextEvent to parcel out time for networking wasn't working
all that well.  Since there can be long delays between WaitNextEvents
due to screen redraws, the networking can be very jittery.  The
intelligent was to use interrupts, which would work during screen
redraws.  

MacTCP offers two types of notification routines.  One routine, the
ASR, calls a function that you specify when new data has arrived, when
the connection is closing, or other similar events.  The second
notification routine is a routine that's called upon completion of an
async routine.

I tried writing the networking using ASR's alone, and another using
async notification routines alone.  Neither worked very well.  The
heartbeat would be exchanged for a while, but eventually a brutal
crash would ensue.  Unfortunately, the crash was never consistent so
it was difficult to debug.

Last night, I decided to rewrite the client end on the Mac to use both
an ASR and an async completion routine.  When the heartbeat arrives,
MacTCP calls my ASR.  In my ASR, I issue an *async* read with a
completion routine pointing to my send routine.  The ASR reads in the
data, and exits fine.  When the async read is done, the completion
routine is called, which then sends back out data.  The heartbeat in
port is different from the heartbeat out port, which would eliminate
any problems MacTCP would have with multiple async commands on the
same port.

This works, but comes to screaching halt on the sixteenth beat.  The
server sends out the sixteenth beat, and nothing ever comes back from
the client.  The client has consistently died on the 16th beat.  By
crash, I mean the mouse no longer responds to movements, but oddly
enough, I'm not tossed into MacsBug.

Entering MacsBug via a hardware interrupt reveals very little.  'SC6'
claims a6 points to a bad stack frame.  'SC7' has the same report
every time, with the last five entries being the same every time.  One
of the entries is labeled, _UnimplTrap.  UnimplementedTrap?  How bad
is that?

I've tried using MacsBug breakpoints to follow my interrupts, rather
than using the Think C debugger.  This is where things get weird.  If
I set an interrupt at my ASR, MacsBug will stop in the ASR when data
arrives.  I always continue since the interrupt supposedly will run 15
more times.  On the second return of the heartbeat from the server,
MacsBug stops in the ASR.  However, when I tell MacsBug to continue
with execution, MacsBug doesn't break into the ASR anymore.  However,
the server does get the heartbeat back.

Obvious problems...

	Am I preserving A5?  The ASR's do that for you, and I've done
so in my async completion code.

	Turned off optimizations in Think C?  Yep.

	Got Think C 5.0.3 for the Quadra?  Yep.

Does MacsBug not deal to well with interrupts?  Has anyone had any
luck with networking at interrupt level?  Has anyone tried to do
anything similar to this?  Do completed async calls somehow linger
around in MacTCP?  My thinking is that 16 beats is such a nice power
of 2, and on the 16th beat, too many things are lingering around.

Any suggestions?

Eric Johnson