home *** CD-ROM | disk | FTP | other *** search
-
- ; LWP.ASM V1.02 18 December 1988
- ; Matthew Dillon
- ;
- ; note: will require some changes for non Aztec assemblers.
- ;
- ; Light Weight Processes
- ;
- ; Note: Calls are not *asynchronously* reentrant. Calls
- ; ARE synchronously reentrant. Please read the docs.
-
- LN_SUCC equ 0
- LN_PRED equ 4
-
- LW_NODE equ 0 ;minimal node, 8 bytes
- LW_STACK equ 8 ;stack ptr used when deallocating it
- LW_STACKSIZE equ 12 ;stack size used when deallocating it
- LW_ALERT equ 16 ;first byte used out of a short
- LW_PC equ 18 ;pc saved on context switch
- LW_REGS equ 22 ;regs saved on context switch
- LW_SIZE equ 22+48 ;12 registers = 48 bytes (D2-D7/A2-A7) to end
-
- LW_A2 equ 24 ;relative to LW_REGS (D2-D7 == 24 bytes)
- LW_A3 equ LW_A2+4
- LW_A4 equ LW_A3+4
- LW_A5 equ LW_A4+4
- LW_A6 equ LW_A5+4
- LW_A7 equ LW_A6+4
-
- LB_ALERT equ 0
- LB_LIMBO equ 1
-
- dseg
-
- ; note: _LastLWPMem takes into account malloc's overhead
- ; by guessing it is 8 bytes.
-
- XDEF _ThisLWP ; user readable (current lwp)
- XDEF _LastLWPMem ; user readable (user information)
- XDEF _CoreLWPStack ; user modifiable
- XDEF _LWPAlloc ; user modifiable memory allocator
- XDEF _LWPFree ; user modifiable memory freer
-
- XREF _lmalloc ; default memory routines used
- XREF _free
-
- _LWPAlloc dc.l _lmalloc ; allocate/free function, can also
- _LWPFree dc.l _free ; set to AllocMem/FreeMem
- _LastLWPMem dc.l 0 ; Last ForkLWP() allocated this much
- _ThisLWP dc.l 0 ; Current LWP
- _CoreLWPStack dc.l 92+8+3 ; 92 for EXEC, 8 for LWP calls if user
- ; specified 0, 3 for long word align
- _MasterStack dc.l 0 ; Global Stack
- _ReadyList dc.l _ReadyList+4 ; LWPs ready to run (list header)
- dc.l 0
- dc.l _ReadyList
-
- cseg
-
- ; All routines marked by A 'R' for 'REENTRANT' can be called
- ; from an LWP. These must work even if the programmer
- ; specifies a 0 stack size. In this case, currently only
- ; 8 extra bytes are available. Thus, these routines must
- ; work with only 8 bytes of stack. These are synchronously
- ; reentrant only.
-
- XREF _LVOInsert
- XREF _LVORemove
- XDEF _ForkLWP ; R
- XDEF _SwitchLWP ; R
- XDEF _WaitLWP ; R
- XDEF _AlertLWP ; R
- XDEF _RunLWP ;
-
- ; RunLWP()
- ;
- ; run all active LWPs until none ready to run. Returns 0
- ; if there are no LWPs ready to run, 1 if there were LWPs
- ; run. This call does not return until the ReadyList is
- ; empty (no LWPs ready to run or all deleted)
-
- _RunLWP: tst.l _ThisLWP ; can't call RunLWP from an LWP.
- bne .rl9
- move.l _ReadyList,A1
- tst.l (A1)
- bne .rl10
- moveq.l #0,D0
- .rl9 rts ; no lwp's ready to run
- .rl10 movem.l D2-D7/A2-A6,-(sp) ; save registers
- pea _RunReturn
- move.l sp,_MasterStack
- bra CtxA1 ; one sided context switch
- _RunReturn: movem.l (sp)+,D2-D7/A2-A6 ; no lwp's to run, but some ran
- moveq.l #1,D0
- clr.l _ThisLWP ; set _ThisLWP to NULL
- rts
-
- ; ForkLWP(stack, arglen)
- ;
- ; This call converts the subroutine that called it into an
- ; LWP by allocating an LWP descriptor and new stack of size
- ; stack+arglen+LOCAL where LOCAL is the stack required by
- ; the subroutine's local variables and saved registers, etc..
- ; (essentially, anything allocated when ForkLWP() was called).
- ;
- ; Note: the subroutine that calls this routine must use the
- ; link/unlk convention with A5 for the link register, and
- ; in addition must NOT destroy any register D2-D7/A2-A6 before
- ; making this call because ForkLWP() will return to the caller
- ; of the subroutine when done rather than the subroutine itself
- ; (the context is setup so when the new LWP is run, it starts
- ; at the point where ForkLWP() would have otherwised normally
- ; returned to the subroutine.
- ;
- ; ForkLWP() may be called by a LWP which effectively replaces
- ; that LWP's stack with another one. In this case, since a
- ; create-new-delete-old sequence occurs, the addresses of
- ; local variables will be different and the old LWP descriptor
- ; will be invalid.
-
- _ForkLWP: link A2,#0 ; 4(A2)=ret addr 8(a2)=stk 12(a2)=arglen
- tst.l _ThisLWP
- beq .il1 ; use master stack if called from a LWP
- move.l _MasterStack,sp
-
- .il1 clr.l _LastLWPMem
- movem.l D2-D7/A2-A6,-(sp) ; Save regs.
- move.l 4,A6 ; ExecBase
-
- move.l 8(A2),D0 ; next longword sized stack.
- add.l _CoreLWPStack,D0 ; required minimum stack, includes 3
- ; for LW align.
- and.b #$FC,D0
- move.l D0,8(A2)
- move.l 12(A2),D0 ; next longword sized arglen.
- addq.l #3,D0
- and.b #$FC,D0
- move.l D0,12(A2)
-
- move.l #LW_SIZE,D0 ; AllocMem the LWP structure
- bsr AllocMyMem
- beq .ilfail
- move.l D0,A3 ; A3 == LWP structure pointer
- clr.b LW_ALERT(A3)
-
- move.w #44-4,D0 ; copy 11 registers to context
- .il5 move.l (sp,D0.W),LW_REGS(A3,D0.W)
- subq.w #4,D0
- bcc .il5
- move.l (A2),LW_REGS+LW_A2(A3)
-
- move.l A5,D2
- sub.l A2,D2
- add.l 12(A2),D2 ; D2 = copysize (A5 - A2 + arglen)
- move.l 8(A2),D0
- add.l D2,D0 ; D0 = total stack size
- move.l D0,LW_STACKSIZE(A3) ; save into lwp structure
- bsr AllocMyMem
- beq .ilfail2
- move.l LW_STACKSIZE(A3),_LastLWPMem
- add.l #LW_SIZE+8,_LastLWPMem
- move.l D0,LW_STACK(A3) ; save into lwp structure
- move.l D0,A0 ; A0 = pointer to stack start
- add.l 8(A2),A0 ; A0 = start of dest copy area
- move.l A0,-(sp) ; (save start of dest copy area)
- lea 8(A2),A1 ; A1 source
- move.l D2,D0
- lsr.l #1,D0 ; D0 = # of words, at least 4
- .il10 move.w (A1)+,(A0)+
- subq.l #1,D0
- bne .il10 ; when done, A0 will be at stack end.
-
- move.l A0,A1
- sub.l 12(A2),A1 ; A1 skip back to lwp subr's ret addr
- move.l #_DeleteLWP,-(A1) ; set ret addr to lwp killer
- move.l A0,-(A1) ; garbage, not required
- move.l A1,LW_REGS+LW_A5(A3); lwp subr's A5 reg.
-
- move.l (sp)+,A0 ; A0 now start of copy area
- move.l A0,LW_REGS+LW_A7(A3); ..is stack ptr on lwp resume
-
- move.l 4(A2),LW_PC(A3) ; ..set pc to ret addr of this routine
-
- lea _ReadyList,A0 ; list
- move.l A3,A1 ; node
- move.l _ThisLWP,A2 ; insert after (ensures this is next run lwp)
- jsr _LVOInsert(A6) ; now a valid lwp.
-
- .ilret move.l A3,D0 ; return the LWP descriptor
- movem.l (sp)+,D2-D7/A2-A6 ; restore registers note
- unlk A2 ; unlink
- rts ; and return (lwp desc)
-
- .ilfail2: move.l A3,A1 ; failure, free the LWP descriptor
- move.l #LW_SIZE,D0
- bsr FreeMyMem
-
- .ilfail: sub.l A3,A3 ; return NULL
- bra .ilret
-
- ; SwitchLWP()
- ;
- ; Switch to next ready LWP (used to share the CPU in tight
- ; loops). Is a fast nop if no other LWPs ready.
- ;
- ; returns 1 if nobody to switch to (the fast nop), 0 otherwise
-
- _SwitchLWP: move.l _ThisLWP,A0 ; current lwp
- move.l LN_SUCC(A0),A1 ; next lwp
- tst.l (A1) ; end of list?
- bne CtxA0A1
- move.l -4(A1),A1 ; yes, get head
- cmp.l A0,A1
- bne CtxA0A1
- moveq.l #1,D0
- rts ; only one lwp ready, let it run
- CtxA0A1 move.l (sp)+,LW_PC(A0) ; switch to next ready lwp
- movem.l D2-D7/A2-A7,LW_REGS(A0)
- CtxA1 movem.l LW_REGS(A1),D2-D7/A2-A7
- move.l LW_PC(A1),A0
- move.l A1,_ThisLWP
- moveq.l #0,D0 ; return 0 (so ForkLWP() returns 0)
- jmp (A0)
-
- ; DeleteLWP() is called from an lwp context. Since we are
- ; deleting it, we can trash any register but A4 which is used
- ; for the small data model base pointer.
- ;
- ; note that we use the MasterStack because we will be making
- ; EXEC calls and do not know how much stack we actually have
- ; left in the LWP.
-
- _DeleteLWP: move.l _MasterStack,sp ; use the master stack since we
- move.l 4,A6 ; will deallocate the LWPs
- move.l _ThisLWP,A2
- move.l A2,A1 ; Remove the lwp
- move.l (A2),A3 ; A3 = next lwp
- jsr _LVORemove(A6)
- move.l LW_STACK(A2),A1 ; then free its stack
- move.l LW_STACKSIZE(A2),D0
- bsr FreeMyMem
- move.l A2,A1
- move.l #LW_SIZE,D0
- bsr FreeMyMem
-
- move.l A3,A1
- tst.l (A1) ; valid next lwp
- bne CtxA1
- move.l _ReadyList,A1 ; no next, get list head
- tst.l (A1)
- bne CtxA1
- rts ; RTS from MasterStack -> RunLWP
-
- ; WaitLWP()
- ;
- ; Wait until alerted. If already alerted this call is
- ; equivalent to a SwitchLWP(). Otherwise, unlink and
- ; then do a SwitchLWP().
- ;
- ; Note that if there are no LWPs ready to run after unlinking,
- ; we return to the overall RunLWP() routine via an RTS from
- ; MasterStack.
-
- _WaitLWP: move.l _ThisLWP,A0
- bclr.b #LB_ALERT,LW_ALERT(A0) ; if lwp already alerted
- bne _SwitchLWP ; yes, don't remove from ready
- ; list, just switch.
- bset.b #LB_LIMBO,LW_ALERT(A0) ; flag as being in limbo
- move.l A0,-(sp) ; Remove(A0)
- move.l LN_SUCC(A0),A1 ; A1 = successor to run
- move.l LN_PRED(A0),A0
- move.l A0,LN_PRED(A1)
- move.l A1,LN_SUCC(A0)
- move.l (sp),A0 ; A0 = guy just removed, now in limbo
- tst.l (A1) ; successor exists?
- bne .wl10 ; yes, switch to new context
- move.l -4(A1),A1 ; no, circular list, get head
- tst.l (A1) ; empty list?
- bne .wl10 ; no, switch to new context
- move.l _MasterStack,sp ; yes, nobody to switch to
- rts
- .wl10 bsr CtxA0A1
- move.l (sp)+,A0 ; (on return)
- bclr.b #LB_ALERT,LW_ALERT(A0) ; clear LB_ALERT again for efficiency
- rts
-
- ; AlertLWP(lwp:4(sp))
- ;
- ; This routine alerts a light weight process, causing it to
- ; be placed on the ready list and also setting the alert
- ; flag so the next WaitLWP() call made by the lwp will fall
- ; through (if it is not already waiting)
-
- _AlertLWP: move.l 4(sp),D0 ; LWP to alert
- beq .al40
- move.l D0,A0
- bclr.b #LB_LIMBO,LW_ALERT(A0) ; in limbo?
- beq .al10 ; no, already on ready list
- lea.l _ReadyList,A1 ; yes, add to ready list
- move.l (A1),(A0) ; node->succ = list->head
- move.l A1,4(A0) ; node->pred = &list->head
- move.l A0,(A1) ; list->head = node
- move.l (A0),A1 ; node->succ->pred = node
- move.l A0,4(A1)
- .al10 bset.b #LB_ALERT,LW_ALERT(A0) ; set alert bit
- .al40 rts
-
- AllocMyMem: movem.l D2/D3/A6,-(sp)
- clr.l -(sp) ; for AllocMem
- move.l D0,-(sp) ; for malloc/AllocMem
- move.l _LWPAlloc,A0
- jsr (A0)
- addq.l #8,sp
- movem.l (sp)+,D2/D3/A6
- tst.l D0
- rts
-
- FreeMyMem: movem.l D2/D3/A6,-(sp)
- move.l D0,-(sp)
- move.l A1,-(sp)
- move.l _LWPFree,A0
- jsr (A0)
- addq.l #8,sp
- movem.l (sp)+,D2/D3/A6
- rts
-
- END
-
-