💾 Archived View for mirrors.apple2.org.za › archive › apple.cabi.net › Languages.Programming › multi… captured on 2023-01-29 at 04:49:41.

View Raw

More Information

-=-=-=-=-=-=-

Newsgroups: comp.sys.apple2.programmer
Path: news.weeg.uiowa.edu!news.uiowa.edu!hobbes.physics.uiowa.edu!math.ohio-state.edu!howland.reston.ans.net!xlink.net!fauern!imp.ch!chsun!bernina!news
From: GUDATH@EZINFO.VMSMAIL.ETHZ.CH (Bright Software)
Subject: Writing code that multitasks
Message-ID: <CCFtnD.H9K@bernina.ethz.ch>
Sender: news@bernina.ethz.ch (USENET News System)
Nntp-Posting-Host: ezinfo
Organization: ETH Zuerich
Date: Fri, 27 Aug 1993 21:42:49 GMT
X-News-Reader: VMS NEWS 1.24
Lines: 155

Net,
 
After posting this silly mail some days ago, I got some inquiries about how
multitasking works. Therefore I'll post some facts about TM. (I don't think
it's wasted bandwidth ;-) )
 
>So how do I make my desktop applications compatible with multitasking a-la
>The Manager?  Do I leave my "GetNextEvent()" loop intact, or do I mask out
>Null Events?
 
>In the Event loop, in a desktop program, can my program have a DoNull
>subroutine, or does my DoNull have to be an RTS and be used solely for
>Manager background tasks.  Or should I completely ignore the DoNull Event
>type.  Thanks for clearing this up for me (since I want to make a program
>I'm working on Manager compatible).
 
The answer is: your program will do those things in the background that are in
the DoNull routine. If DoNull is only "rts", then you won't be able to
multitask.
 
[Note: you can use TaskMaster or GetNextEvent - both work equally well with TM]
 
The Manager processes programs in the background in discrete steps - only the
code between two GetNextEvents (GNE) will be executed "at once" ( :-) ).
Needless to say, TM doesn't care if the two GNE are two distinct toocalls or
actually only one around which the program loops. Though this seems to be a
silly remark, this leads to two different implementations of
multitasking-compatible code.
 
Multitasking with "DoNull"
 
If you're a perfectionist, you need the ultra-superduper-DoNull mechanism. This
method, however, can turn the main event loop into the worst piece of software
because it might become seriously complex. Anyway...
 
The usual desktop app(-lication) contains one single event loop. All user
interactions go through this loop:
 
EventLoop       TaskMaster(this&that)
                pla
                asl
                tax
                jsr (:DoSomething,x)
                bra EventLoop
:DoSomething    dw      DoNull  ;no actions
                dw      InMenu,InContent,Control...etc
 
DoNull          inc x   ;increment x
                rts
InMenu          ...
InContent       ...
....             ...
 
Normally, the DoNull routine returns immediately - because, as the name
implies, there is nothing to do. Under TM, however, this subroutine is the only
part of background programs that will be executed.
The reason is simple: when the active app is not busy, it loops around in its
own GNE loop and receives only Null events. In such situations, TM passes
control to one background program during the GNE toolcall located in the active
application. The GNE where the _background_ program was interrupted finally
returns NIL, therefore DoNull is being called. (DoNull returns then to the main
event loop, TM switches back to the front app etc etc)
 
As you can see, DoNull must do *something* in order to mutlitask. Two warnings
at this point: what it does must be done very quickly, otherwise the
environment becomes sluggish. Secondly, you must be carefull with GrafPorts -
restore the GrafPort that was active before returning!!
 
The problem: you have to modify a separate, continuous routine so that it can
be processed in discrete steps. As an example, imagine you have a telecomm
program and have started a macro. You could, for example, take one single macro
command, process it, and then return. During the second visit of your app, take
the second one, etc etc.
Of course, since the interesting stuff is happening in the event loop you have
to add some flags because the user can select more functions before you have
actually terminated the first one. (Dim the corresponding menu items, for
example.)
 
Lazy programmer's cheap trick (my favorite:-))
 
Another possibility is adding pseudo-taskmasters in your routines. This is much
simpler than the method above.
Insert here and there a TaskMaster with a _NIL_ event mask. Make sure you do
not pass a pointer to your main event record. When modifying existing routines,
you often refer to values found in this record, and by inserting pseudo
taskmasters you overwrite those paramaters. (It takes hours to find this bug,
believe me..:)). Of course, carefully place the taskmasters so that the code
between two TaskMasters does not require too much CPU time but accomplishes
enough to justify the significant overhead of switching apps.
 
Furthermore, don't forget to add a few TaskMasters with a "update event" mask.
Remember, users can switch back and forth, and each time the desktop must be
redrawn. Use NIL masks in tight loops, and then handle update events in outer
loops. If I may suggest a routine:
 
FirstMultiTask                  ;call this routine first
                _GetPort
                sta MyPort
                _TaskMaster(NIL)
                _GetPort
                sta OtherPort
                _SetPort(MyPort)
                rts
MultiTask                       ;then continue calling this one
                lda  PacketSize
                and  #%1
                beq  :always
                lda  PacketSize
                eor  #%10
                sta  PacketSize
                and  #%10
                bne  :skip
:always         _GetPort
                sta MyPort
                _SetPort(OtherPort)
                _TaskMaster(NIL)
                _GetPort
                sta OtherPort
                _SetPort(MyPort)
 
:skip           rts
 
LastMultiTask                   ;before returning from your routine, call me
                _GetPort
                sta MyPort
                _SetPort(OtherPort)
                _TaskMaster(NIL)
                rts
 
When a particular routine takes over control, execute the first routine. Then,
sporadically, call MultiTask. At the end, jsr to LastMultiTask. The three
routines save and restore GrafPorts properly - if you do not depend on them,
great! (Note: You should call TaskMaster(Update) from time to time - the above
code does not care about updates...)
 
The middle routine also contains another feature. If PacketSize=%1, then only
each second call to this routine will actually call TaskMaster. I recommend you
add a check box somewhere in your program where the user can switch between the
two speeds - owners of accelerated computers will be most thankfull....
(tasks need far less time because lots of app switching is not needed...)
 
Feel free to mail me if things are not clear!
 
henrik
 
 
[PS: no, GNE is not a UNIX port-over...:)))]

  ___________________________________________________________________
 |                                                                   |
 | Andre Horstmann         |  I am not Henrik, and Henrik is not me! |
 | GEnie A.HORSTMANN       |  Ask me about ShadowWrite!  Apple IIGS? |
 | Internet: shadow@       |  Hmmm - nicht immer, aber immer oefter! |
 | ezinfo.vmsmail.ethz.ch  |  There's still a glittering in my eyes..|
 |___________________________________________________________________|