Article View: comp.sys.amiga.tech
Article #10601Could somebody tell me ...
From: Matt Dillon
Date: Thu, 15 Dec 1988 19:37
Date: Thu, 15 Dec 1988 19:37
122 lines
5413 bytes
5413 bytes
Which register or registers are used as the link register by
Lattice C? I need to know so I can support both Aztec and Lattice in the
following: I just thought up an as-yet untried method of programming on
the amiga. Namely, very light weight synchronous processes. The idea works
like this:
main()
{
task1(); /* start the first lwp */
task2(); /* start a second lwp */
task1(); /* start a third lwp using the same code as the first */
run_lwp(); /* run lwp's until hell freezes over */
}
task1()
{
startlwp(n);
<initialization goes here>
if (initlwp())
return(init_error_code);
while (notdone) {
....
waitlwp();
}
endlwp();
}
task2()
{
startlwp(n);
<initialization goes here>
if (initlwp())
return(init_error_code)
while (notdone) {
....
waitlwp();
}
endlwp();
}
Now, who can figure out what I am talking about here? The idea is
for the initial call from main() to *create* a light weight process context
for the subroutine and to then allow main() to continue and call other
lwp's to create them. Thus, a direct call to task1() or task2() must return
almost immediately so other calls can be made. This also means that you can
create multiple lwp's using the same code (task?()) simply by calling it.
startlwp() allocates a copy of the stack accessable to the subroutine.
Specifically, M+N bytes are allocated where M is the number of bytes already
being used by the subroutine (calculated via the current sp and the link
register), and N is an additional amount that you will need for calls you
make inside. startlwp() copies the subroutines stack data into the new
stack, creates a light weight process descriptor and adds it to a global
lwp list of some sort. The new stack pointer now becomes the stack's sp.
You then have your initialization code. After this code, you must
have: if (initlwp()) return(error_code). initlwp() returns TRUE when it
is first called from a newly created descriptor and thus task?() will
return back to main(). initlwp() also saves the state in the newly
created descriptor so when the light weight process system begins to run,
the context will come back to initlwp() (which will then return FALSE so
you drop into your lwp loop). After that, initlwp() is never called again.
The new task?() is now fully light weight. main() goes ahead and
starts up a couple more of these babies, and then finally calls run_lwp(),
which automatically cycles through those lwp's which are ready to go. This
cycling is completely synchronous in that a lwp does not get interrupted,
and can only loose cpu when it does a waitlwp() call.
When the lwp is ready to die, it calls endlwp() (which returns the
calling context and creates a stack frame that looks like the original call
that started it all), and you then simply return.
p.s.: run_lwp() does not call the entry point of the lwp() .. it
always restores a context and then jumps to it. Since waitlwp() must be
called to cause a context switch, only A2-A7/D2-D7/PC need be saved. A
context switch is thus essentially:
move.l _CurrentLWP,A0 ;descriptor or current lwp
move.l ln_Next(A0),A1 ;next lwp to run
tst.l (A1) ;end of list
beq somewhereelse
.1 movem.l D2-D7/A2-A7,wp_Context(A0) ;save current context
move.l (sp)+,wp_PC(A0) ;save pc at call
move.l A1,_CurrentLWP ;set next lwp
movem.l wp_Context(A1),D2-D7/A2-A7 ;restore context
move.l wp_PC(A1),-(sp) ;get pc for next lwp
rts
somewhereelse: ; circular list, get head.
move.l -4(A1),A1
cmp.l A0,A1 ; only one lwp ready to run?
bne .1
rts ; yes (actually, we would probably want to
; do a real _Wait() here).
Not bad, eh? So, why would you want to do this when the amiga has
multitasking? Because this allows you to modularize your code without
the overhead of a task for each module! The context switch is much faster
than with real tasks.
But the real reason is to get out of the main-loop driven standard
of programming (mac users are fond of quoting the fact that multitasking
does not avoid the need for a main-event-wait-loop). Here, one would
be able to segment a program into a lot of little programs each waiting for
a different kind of event (or handling a different part of the data stream).
Not only can you break up a program into a set of synchronously running
lwps, but you can create real task contexts around pretty much arbitrary
groups of lwps without having to modify the code associated with each
module (if written right).
So, I need ideas on the waiting part (that waits for some even and
does the context switching). We need something that nicely complements
Wait(), but in the lwp sense. Think about various types of events and
things that one might want to wait for and then figure out an efficient
way to handle N lwps each waiting for one type of event such that one does
not need to poll.
-Matt
Technical Details
Message-ID:
<8812151937.AA17954@postgres.Berkeley.EDU>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!attcan!uunet!lll-winken!lll-tis!ames!pasteur!ucbvax!POSTGRES.BERKELEY.EDU!dillonShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!attcan!uunet!lll-winken!lll-tis!ames!pasteur!ucbvax!POSTGRES.BERKELEY.EDU!dillon From: dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) Newsgroups: comp.sys.amiga.tech Subject: Could somebody tell me ... Message-ID: <8812151937.AA17954@postgres.Berkeley.EDU> Date: 15 Dec 88 19:37:28 GMT Sender: daemon@ucbvax.BERKELEY.EDU Lines: 121