Path: 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 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); if (initlwp()) return(init_error_code); while (notdone) { .... waitlwp(); } endlwp(); } task2() { startlwp(n); 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