[comp.sys.amiga.tech] Could somebody tell me ...

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (12/16/88)

	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

rminnich@super.ORG (Ronald G Minnich) (12/17/88)

In article <8812151937.AA17954@postgres.Berkeley.EDU> dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) writes:
> Some interesting stuff on lwp functions he is proposing.
Two thoughts: 
1) Have you looked at the way either Sequent's microtasking library or the 
   Spoc multiprocessor did it? 
Seems to me you want to create a set of context's for a set of lwps, then
fire up the lwp's using those pre-created segments. Bot Spoc and the Sequent
system do this well.
>	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).
2) But doesn't there have to be a loop somewhere? I can't figure out how to make
it go away. There has to be something waiting for events and then dispatching
them. Or each of your lwp's has to poll, doesn't it? Even select() in the 
Unix kernel does polling in some form. I just can't see how to avoid it.
   What am i missing?
ron

andy@cbmvax.UUCP (Andy Finkel) (12/17/88)

In article <8812151937.AA17954@postgres.Berkeley.EDU> dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) writes:
>	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

Hmmm....sounds like coroutines

>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
>	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

yup, we're talking coroutines 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

>	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.

we call it cowait()

You are well underway to reinventing coroutines, Matt :-)

What are coroutines ?  Well, they are pretty much as Matt is on
the way to describing them...lightweight processes that operate
synchroniously, using your stack.  The Amiga file system uses
them extensively.  They are a pretty clever idea.  They are also
found in Workbench.  I think Neil Katin was sufficiently impressed
with the concept that he used that model of programming internal
to Workbench.
>					-Matt


-- 
andy finkel		{uunet|rutgers|amiga}!cbmvax!andy
Commodore-Amiga, Inc.

"Possibly this is a new usage of the word 'compatible' with which
 I was previously unfamiliar"

Any expressed opinions are mine; but feel free to share.
I disclaim all responsibilities, all shapes, all sizes, all colors.