[comp.sys.mac.programmer] Questions about multitasking on the mac

jtn@potomac.ads.com (John T. Nelson) (05/12/91)

No I don't want to start yet ANOTHER flamewar on what type of
multitasking is the One And True Multitasking.  Rather I have a
legitimate question concerning how one passes control onto other apps
running cooperatively on the Mac.  Let's say I have a large and
complex compiler (which I do) and let's say that it consists of lots
of hairy recursive-decent style code.  It's difficult to modularize
this particular code so I can't call lots of modules from the event
loop.  Basically we go into the compiler and come back out when done.

This means that I don't get to call WaitNeXTEvent and thus the
foreground process has total control.  No backgrounding possible
because we don't get to the event loop until complete.

Now, as near as I can tell the only way to provide backgrounding of
this sort would be to do something like the following in EACH AND
EVERY MODULE that I have in the compiler....

a_module(argument1, argument2)
{
blah();
foo();
while( flagSet ) {
	bar();
	baz();
}

DO_WAIT_NEXT_EVENT();
return;

bar()
{
.
.
.
DO_WAIT_NEXT_EVENT();
return;
}


and so on and so on.  In the function DO_WAIT_NEXT_EVENT I have
basically the same calls that I have in the top level event loop.  We
call WaitNeXTEvent and process various events.

I suppose this can be done but its GROSS infusing this event code in
every module to get timely event updates.  I can't figure out a better
way unfortunately.  Anyone have a different philosophy on how this
could be accomplished?

n67786@cc.tut.fi (Tero Nieminen) (05/12/91)

In article <1991May11.191114.27564@potomac.ads.com> jtn@potomac.ads.com (John T. Nelson) writes:


   No I don't want to start yet ANOTHER flamewar on what type of
   multitasking is the One And True Multitasking.  Rather I have a
   legitimate question concerning how one passes control onto other apps
   running cooperatively on the Mac.  Let's say I have a large and
   complex compiler (which I do) and let's say that it consists of lots
   of hairy recursive-decent style code.  It's difficult to modularize
   this particular code so I can't call lots of modules from the event
   loop.  Basically we go into the compiler and come back out when done.

I had this same problem with a cde that had a lengthy sort procedure.
And for the speed I used quick sort which is recursive. In effect one
has to include one WaitNextEvent call to every recursion that the code
takes in this case to both two halves of the quick sort recursion.

In order to prevent the sort calling WaitNextEvent in every recursion,
which would be way too much and allso slow down the sort unnecessarily,
I capsulated the sort into a module and made a module global that keeps
a count of the times the sort has been recursing since last
WaitNextEvent, and only call it if enough sorting has taken place. In
practice every recursion increments the variable and when a preset value
is reached WNE is called and the variable reset.

Actually I did call a bit more than just WNE. If I recall correctly the
reason why a simple WNE was not enough was that the window of my program
would not redraw itself after switching between background and
foreground or something of that nature. Probably the best way to to do
something like that is to pass a procedure as a parameter to be called
often enough during such lengthy operations.

In any case it surely doesn't make modularising programs very simple
neither too clean..

-- 
   Tero Nieminen                    Tampere University of Technology
   n67786@cc.tut.fi                 Tampere, Finland, Europe

jtn@potomac.ads.com (John T. Nelson) (05/12/91)

Calling WaitNeXTEvent and the DoEvent stuff fromwithin your program
code "often" seems to be the way that multitasking is done on the Mac.
Is this the way that ZTerm manages to do it's thing and yet be
backgroundable?  I notice that Compactor Pro also is "backgroundable"
in the sense that you can compact files and throw the thing into the
background by bringing a new application up front and still have
compactor do its thing.  Is this the way Compactor implements this
behavior?  Just call WaitNeXTEvent and the DoEvent servicing stuff
inside a convenient  loop in the compaction algorithm?

dickie@schaefer.math.wisc.edu (Garth Dickie) (05/13/91)

In article <1991May11.191114.27564@potomac.ads.com> jtn@potomac.ads.com (John T. Nelson) writes:
>
>I suppose this can be done but its GROSS infusing this event code in
>every module to get timely event updates.  I can't figure out a better
>way unfortunately.  Anyone have a different philosophy on how this
>could be accomplished?
>
As has been mentioned, you can avoid losing too much time, by counting how long
it has been since a WaitNextEvent.  This still means changes throughout your
code.  The inner event call has to be fairly restictive to the user, and you
still need to handle update events, etc.  When you want to improve the user
interaction later,  you are still stuck with a redesign.  This gives you some
minimal backgrounding ability, but doesn't buy you much else.

To really move to a single event loop, you probably have to find a way to
modularize your code.  I have tried this in some projects, and it definitely
simplifies the user's view of the program.  It can enforce some orthoganality
that might otherwise be abandoned.  Of course, when I did this there was not
as much state to preserve across computations as a compiler would need.  It
appears that in general, this is a hard problem :-(

Of course, MPW tools seem to solve it, but I think they use the first model,
calling out to a timeslicer.  Anybody who's designed an MPW compiler tool care
to comment?
-- 
Garth Dickie -- Confused Math Graduate Student -- dickie@math.wisc.edu

ech@cbnewsk.att.com (ned.horvath) (05/13/91)

From article <1991May12.150246.6287@potomac.ads.com>, by jtn@potomac.ads.com (John T. Nelson):
> 
> Calling WaitNeXTEvent and the DoEvent stuff fromwithin your program
> code "often" seems to be the way that multitasking is done on the Mac.
> Is this the way that ZTerm manages to do it's thing and yet be
> backgroundable?  I notice that Compactor Pro also is "backgroundable"
...

I didn't write either of these, but I've done similar kinds of apps...

The wait-now-and-then method is about all there is for essentially batch
processes like compaction (and compilation, linking, spreadsheet recalcs,
etc...).  Highly interactive programs call WNE anyway.  Comm programs are
running in the background because either they aren't busy, or they're doing
file transfers, and they're being clocked by external I/O.  It's fairly 
easy (well, I've been doing I/O programming for too many years, so *I*
think it's easy) to do file transfers driven by I/O complete events, 
which don't go through the usual Toolbox event queue.

To sketch the process, doing an XMODEM upload basically requires you to do
a disk read (which can be async if *ahem* your disk I/O can support async),
then a serial port write (async), then a serial port read (for the ack;
async, again) then either a retransmit (NAK) or start again with the disk
read.

The last paragraph is oversimplified, it's not intended to be a detailed 
design, but you can see how it's another event-driven process that really
does go on in parallel with whatever processes MultiFinder is managing.
A more sophisticated implementation would have a producer (disk read) feeding
a consumer (serial write) both free-running on I/O completes.  Assuming
ZTERM is this clever, there are actually three threads in the ZMODEM memory
space: disk I/O, serial I/O, and the user interface.
-- 

=Ned Horvath=
ehorvath@attmail.com

Lawson.English@p88.f15.n300.z1.fidonet.org (Lawson English) (05/13/91)

John T. Nelson writes in a message to All

JTN> I suppose this can be done but its GROSS infusing this event 
JTN> code in every module to get timely event updates. I can't figure 
JTN> out a better way unfortunately. Anyone have a different philosophy 
JTN> on how this could be accomplished?

Do you want responsiveness to events concerning the operation of your compiler
(like <cmd-period>s and the like) or do you simply want to allow some background
time?

If the latter, then a call to WaitNextEvent, without processing the event, will
give the background applications some time (although not much!).

If you simply want to be able to cancel operations (as well as allow *some*
backgrounding), simply checking for <cmd-periods> in your Do_Wait_Next_Event
function would be sufficient (or so I would think).


Lawson
 

--  
Uucp: ...{gatech,ames,rutgers}!ncar!asuvax!stjhmc!300!15.88!Lawson.English
Internet: Lawson.English@p88.f15.n300.z1.fidonet.org

peirce@outpost.UUCP (Michael Peirce) (05/13/91)

In article <1991May12.194909.18585@schaefer.math.wisc.edu>, dickie@schaefer.math.wisc.edu (Garth Dickie) writes:
>
> Of course, MPW tools seem to solve it, but I think they use the first model,
> calling out to a timeslicer.  Anybody who's designed an MPW compiler tool care
> to comment?


Yes, that's how it's done.  MPW tools call a library routine called
SpinCursor (there are a couple of variations of this).  This allows
them to yield time and also to indicate to the user that work is being
done.

-- michael

--  Michael Peirce         --   outpost!peirce@claris.com
--  Peirce Software        --   Suite 301, 719 Hibiscus Place
--  Macintosh Programming  --   San Jose, California 95117
--           & Consulting  --   (408) 244-6554, AppleLink: PEIRCE

time@ice.com (Tim Endres) (05/14/91)

In article <1991May12.150246.6287@potomac.ads.com>, jtn@potomac.ads.com (John T. Nelson) writes:
> behavior?  Just call WaitNeXTEvent and the DoEvent servicing stuff
> inside a convenient  loop in the compaction algorithm?

The alternative (which seems cleaner) is to drive a state machine
within the event loop. This is how uAccess works for all of its 
communications, background or foreground. There are, however, still
a few spots where inner loops call WaitNextEvent().

-------------------------------------------------------------
Tim Endres                |  time@ice.com
ICE Engineering           |  uupsi!ice.com!time
8840 Main Street          |  Voice            FAX
Whitmore Lake MI. 48189   |  (313) 449 8288   (313) 449 9208

lsr@Apple.COM (Larry Rosenstein) (05/15/91)

In article <1991May11.191114.27564@potomac.ads.com> jtn@potomac.ads.com (John T. Nelson) writes:
>
>This means that I don't get to call WaitNeXTEvent and thus the
>foreground process has total control.  No backgrounding possible
>because we don't get to the event loop until complete.

You can always yield the CPU by calling WaitNextEvent with a null mask.
This will give background tasks a chance to run.  Since you pass a null
mask, you won't get back any events, and the user won't be able to put your
app in the background.

If you want to be able to run in the background, then  have to do more work.
You will have to use a non-null mask, and actually process any events that
you receive.  You will also have to filter out events/actions that don't
make sense while your program is running.

>I suppose this can be done but its GROSS infusing this event code in
>every module to get timely event updates.  I can't figure out a better

One solution if you are using MPW 3.2 (don't know about THINK C) is to take
advantage of the new -trace on flag.  This was added to correspond to the
feature in MPW Object Pascal.  

What it does is insert call to %_BP and %_EP at the beginning and end of
each routine.  You can then use the linker's ability to rename modules to
direct these calls to a routine that handles the events.  If your program
does a lot of procedure calls, this may be sufficient.  Otherwise, you might
have to manually insert additional calls in other loops.  It's also possible
to mechanically insert calls in all the loops.

Another thing you should do is record the time between calls to this
routine, and only call WaitNextEvent if sufficient time has elapsed.
Calling WaitNextEvent more frequently simply slows down your application,
without improving the system's responsiveness.

I've used this technique to port the PBMPLUS image processing package to
MPW, and get it to run well in the background.  In that environment, you can
even use shell variables to control how often the program yields the CPU.

-- 
Larry Rosenstein, Apple Computer, Inc.

lsr@apple.com
(or AppleLink: Rosenstein1)

lsr@Apple.COM (Larry Rosenstein) (05/15/91)

In article <1991May12.194909.18585@schaefer.math.wisc.edu> dickie@math.wisc.edu (Garth Dickie) writes:
>In article <1991May11.191114.27564@potomac.ads.com> jtn@potomac.ads.com (John T. Nelson) writes:
>>
>Of course, MPW tools seem to solve it, but I think they use the first model,
>calling out to a timeslicer.  Anybody who's designed an MPW compiler tool care
>to comment?

In an MPW tool you simply call one of the cursor routines
(SpinCursor/RotateCursor) to yield the CPU.  The MPW Shell takes care of
processing events.-- 
Larry Rosenstein, Apple Computer, Inc.

lsr@apple.com
(or AppleLink: Rosenstein1)

amanda@visix.com (Amanda Walker) (05/19/91)

In article <1CE00001.fiq1tb@tbomb.ice.com> time@ice.com (Tim Endres) writes:

   The alternative (which seems cleaner) is to drive a state machine
   within the event loop.

I use this style myself.  A extra bonus is that it makes it easier to
build multithreaded applications (if this doesn't make sense to you,
think "internal multitasking" within an application), since nothing ever
blocks.

This does require a different style of structuring your code, but it
doesn't take too long to get the hang of it.  This is sometimes also
called "continuation-passing style".
--
Amanda Walker						      amanda@visix.com
Visix Software Inc.					...!uunet!visix!amanda
-- 
"For historical reasons, these hints are indicated by names that contain
 the word "Blue."	-- Adobe Type 1 Font Format

glenn@gla-aux.uucp (Glenn Austin) (05/19/91)

In article <1991May11.191114.27564@potomac.ads.com>, jtn@potomac.ads.com (John T. Nelson) writes:
> 
> No I don't want to start yet ANOTHER flamewar on what type of
> multitasking is the One And True Multitasking.

Thank you.
> Rather I have a
> legitimate question concerning how one passes control onto other apps
> running cooperatively on the Mac.  Let's say I have a large and
> complex compiler (which I do) and let's say that it consists of lots
> of hairy recursive-decent style code.  It's difficult to modularize
> this particular code so I can't call lots of modules from the event
> loop.  Basically we go into the compiler and come back out when done.
> 
> This means that I don't get to call WaitNeXTEvent and thus the
> foreground process has total control.  No backgrounding possible
> because we don't get to the event loop until complete.

Make calls to EventAvail periodically -- the callee of EventAvail (OSEventAvail)
is where MultiFinder patches in to get time...


===============================================================================
| Glenn L. Austin                | "Turn too soon, run out of room.           |
| Macintosh Wizard and           |    Turn too late, much better fate."       |
| Auto Racing Driver             |   -- Jim Russell Racing School Instructors |
|-----------------------------------------------------------------------------|
| Usenet:  glenn@gla-aux.uucp         | CI$:       76354,1434                 |
| GENie:     G.AUSTIN3                | AOnline:   GAustin                    |
===============================================================================