DIRECTORY Allocator USING[FNHeaderP, NHeaderP, RefCount, maxSmallBlockSize, BlockSizeIndex, bsiEscape, NormalHeader], AllocatorOps USING[REFToNHP, NHPToREF], Basics USING[BITAND, BITOR, BITSHIFT, BITXOR, HighHalf, LowHalf], Collector USING[Disposition], PrincOpsUtils USING[ZERO], RCMicrocodeOps USING[], SafeStorage USING[Type, nullType], ZCT USING[FOSTableIndex, FOSTableLength, FOSTableResidue, zctBlockWords, ZeroCountTable, zct, ZCTObject, FOSTableObject]; RCMicrocodeImpl: PROGRAM IMPORTS AllocatorOps, Basics, PrincOpsUtils, ZCT--zct-- EXPORTS RCMicrocodeOps = BEGIN OPEN Allocator, Collector, ZCT; checking: BOOL = FALSE; RCOverflowOccurred: PUBLIC ERROR = CODE; RCUnderflowOccurred: PUBLIC ERROR = CODE; LookFurtherAtReclaimedRef: PUBLIC ERROR = CODE; ZCTFull: PUBLIC ERROR = CODE; NormalFreeListEmpty: PUBLIC ERROR = CODE; ALLOCATE: PUBLIC --INTERNAL-- PROC[--requested--size: CARDINAL, type: SafeStorage.Type] RETURNS[r: REF _ NIL] = { IF size <= maxSmallBlockSize THEN { bsi: BlockSizeIndex = zct.sizeToBSI[size]; fnhp: FNHeaderP _ zct.bsiToFreeList[bsi]; nhp: NHeaderP; IF fnhp = NIL THEN ERROR NormalFreeListEmpty; nhp _ @fnhp.fnh; r _ AllocatorOps.NHPToREF[nhp]; OnZ[nhp]; -- may pageFault or raise ZCTFull; ifso, no changes have been made zct.bsiToFreeList[bsi] _ fnhp.nextFree; fnhp.nextFree _ NIL; -- CLEAR THE NEW OBJECT nhp.type _ type; nhp.maybeOnStack _ zct.markingDecrements; }; }; FREEPLEASE: PUBLIC --INTERNAL-- PROC[nhp: NHeaderP] RETURNS[success: BOOL _ FALSE] = { bsi: BlockSizeIndex = nhp.blockSizeIndex; IF (success _ bsi # bsiEscape) THEN { fnhp: FNHeaderP = LOOPHOLE[nhp, FNHeaderP]; PrincOpsUtils.ZERO[nhp + SIZE[NormalHeader], zct.bsiToSize[bsi] - SIZE[NormalHeader]]; fnhp.fnh.type _ SafeStorage.nullType; -- mark the object as free fnhp.nextFree _ zct.bsiToFreeList[bsi]; zct.bsiToFreeList[bsi] _ fnhp; }; }; ASSIGNREF: PUBLIC --INTERNAL-- PROC[rhs: REF, lhs: LONG POINTER TO REF] = { lhsRef: REF; rnhp: NHeaderP; rrc: RefCount; lhsRef _ lhs^; IF rhs # NIL THEN { IF lhsRef = rhs THEN RETURN;--no-op. Bug: x_x. Either do this check or re-read rrc below rnhp _ AllocatorOps.REFToNHP[rhs]; -- subtract 2 rrc _ rnhp.refCount; IF rrc = LAST[RefCount] THEN ERROR RCOverflowOccurred; }; IF lhsRef # NIL THEN { lnhp: NHeaderP = AllocatorOps.REFToNHP[lhsRef]; lrc: RefCount _ lnhp.refCount; CheckForRCUnderflow[lnhp]; -- might raise RCUnderflowOccurred IF zct.markingDecrements OR (lrc = 1 AND NOT lnhp.rcOverflowed) THEN OnZ[lnhp]; -- might raise ZCTFull lnhp.maybeOnStack _ zct.markingDecrements--ASSUME zct^ is pinned--; lnhp.refCount _ lrc - 1; }; IF rhs # NIL THEN { rnhp.refCount _ rrc + 1; -- COMMITTED NOW to finish this call rnhp.maybeOnStack _ FALSE; }; lhs^ _ rhs; -- NOT counted }; -- end ASSIGNREF OnZ: PUBLIC --INTERNAL-- PROC [nhp: NHeaderP] = { wp: LONG POINTER TO NHeaderP; IF nhp.inZCT THEN RETURN; wp _ zct.wp; wp^ _ nhp; -- might pageFault IF Basics.BITAND[Basics.LowHalf[LOOPHOLE[wp, LONG CARDINAL]], zctBlockWords - 1] = zctBlockWords - 2*SIZE[LONG POINTER] THEN { -- wp points to the cell before the link to the next zctBlock IF (wp _ LOOPHOLE[(wp+SIZE[LONG POINTER])^, -- pick up the link LONG POINTER TO NHeaderP]) = NIL THEN ERROR ZCTFull -- i.e. XXX TRAP with trapParam = 4 ELSE zct.wp _ wp; } ELSE zct.wp _ wp + SIZE[LONG POINTER]; -- will NOT pageFault nhp.inZCT _ TRUE; -- will NOT pagefault }; CREATEREF: PUBLIC --INTERNAL-- PROC[nhp: NHeaderP] = { IF checking AND (nhp.refCount # 0 OR nhp.rcOverflowed) THEN ERROR; OnZ[nhp]; -- may pageFault or raise ZCTFull; ifso, no changes have been made nhp.maybeOnStack _ zct.markingDecrements; }; RECLAIMABLEREF: PUBLIC --INTERNAL-- PROC[nhp: NHeaderP] RETURNS[Disposition] = { RETURN[MCRECLAIMABLEREF[nhp, FALSE]]; }; MCRECLAIMABLEREF: PROC[nhp: NHeaderP, decrPending: BOOL] RETURNS[d: Disposition _ continue] = { rc: RefCount _ nhp.refCount; IF decrPending THEN rc _ rc - 1; -- won't underflow IF rc # 0 OR nhp.inZCT OR nhp.rcOverflowed THEN RETURN; -- leave it alone IF nhp.maybeOnStack OR FoundInFHSnapshot[nhp] THEN OnZ[nhp] -- put it back; this may pageFault or raise ZCTFull ELSE d _ (IF nhp.f THEN finalizeIt ELSE reclaimIt); }; FoundInFHSnapshot: PROC[nhp: NHeaderP] RETURNS[found: BOOL _ FALSE] = { index: FOSTableIndex; residue, entry: FOSTableResidue; fosTable: LONG POINTER TO FOSTableObject = LOOPHOLE[zct+SIZE[ZCTObject], LONG POINTER TO FOSTableObject]; [index, residue] _ FOSTableHash[nhp]; entry _ fosTable[index]; RETURN[residue = Basics.BITAND[residue,entry]]; }; FOSTableHash: PUBLIC PROC[nhp: NHeaderP--maybe bogus--] RETURNS[x: FOSTableIndex, r: FOSTableResidue] = { OPEN Basics; lc: LONG CARDINAL = LOOPHOLE[nhp, LONG CARDINAL]; u: FOSTableResidue; r _ BITOR[BITSHIFT[HighHalf[lc], 3], BITSHIFT[LowHalf[lc], -13]]; u _ BITAND[r, zct.residueMask]; x _ BITAND[BITXOR[BITSHIFT[LowHalf[lc], -1], u], FOSTableLength-1]; }; RECLAIMEDREF: PUBLIC -- INTERNAL-- PROC[ref: REF] RETURNS[ans: REF ANY _ NIL] = { nhp: NHeaderP = AllocatorOps.REFToNHP[ref]; CheckForRCUnderflow[nhp]; -- might raise RCUnderflowOccurred SELECT MCRECLAIMABLEREF[nhp, TRUE] FROM continue => NULL; reclaimIt => ans _ ref; finalizeIt => ERROR LookFurtherAtReclaimedRef; --ReclaimedRef MISC TRAP, trapParam = 6 ENDCASE => ERROR; nhp.refCount _ nhp.refCount - 1; }; CheckForRCUnderflow: PROC[nhp: NHeaderP] = { rc: RefCount _ nhp.refCount; IF checking AND rc = 0 AND NOT nhp.rcOverflowed THEN ERROR; IF rc = 0 --was RCMicrocodeOps.rcBottom-- AND nhp.rcOverflowed THEN ERROR RCUnderflowOccurred; -- no changes have been made }; DISABLEMICROCODE: PUBLIC -- INTERNAL-- PROC[z: ZeroCountTable] = { NULL; -- the real uc: ucDisabled _ TRUE }; ENABLEMICROCODE: PUBLIC -- INTERNAL-- PROC[z: ZeroCountTable] RETURNS[ucVersion: NAT] = { RETURN[0]; -- the real uc: ucDisabled _ FALSE }; END. RCMicrocodeImpl.mesa Software Implementation of the Cedar reference-counting microcode Paul Rovner, January 13, 1984 4:34 pm Bob Hagmann, May 10, 1984 4:26:55 pm PDT all but FOSTableHash are really INTERNAL procs of the ZCTImpl monitor FLAGS ucDisabled: BOOL; private flag kept in ucode registers ERRORS PROCEDURES Software implementation of the Allocate MISC zct must also include: sizeToBSI, bsiToFreeList, bsiToSize Returns NIL IF size> Allocator.maxSmallBlockSize may pageFault or raise ZCTFull or NormalFreeListEmpty; ifso, no changes have been made IF ucDisabled THEN Allocate MISC TRAP with trapParam = 2 Allocate MISC TRAP with trapParam = 6 XXX touch nhp.type, .maybeOnStack, zct.markingDecrements, fnhp.nextFree COMMITTED if here Software implementation of the Free MISC Returns FASE IF bsi = bsiEscape may pageFault; ifso, no changes have been made IF ucDisabled THEN Allocate MISC TRAP with trapParam = 2 Software implementation of the AssignRef and AssignRefNew opCodes This might pageFault or raise ZCTFull, RCOverflowOccurred or RCUnderflowOccurred If this guy pageFaults or raises a signal no changes have been made IF ucDisabled THEN opCode TRAP with trapParam = 2 lhs _ AddAlphaByte[lhs, XXX]; check first for rcoverflow of rhs; after changes are made to lhs we must guarantee completion i.e. opCode TRAP with trapParam = 1 increment, assignment were NOT done The handler for RCOverflowOccurred makes adjustments, not changes Everything that might pageFault has been touched when we get here COMMITTED NOW to finish this call Everything that might pageFault has been touched when we get here. Software implementation of the OnZ microcode subroutine This can pageFault or raise ZCTFull. If so, no changes have been made. This is the only proc that explicitly raises ZCTFull changes zct.wp sets nhp.inZCT _ TRUE wp will not point to the link word; wp^ may be left with garbage if ZCTFull is raised now COMMITTED to finish this call Software implementation of the CreateRef MISC IF ucDisabled THEN CreateRef MISC TRAP with trapParam = 2 Software implementation of the ReclaimableRef MISC This may pageFault or raise ZCTFull; ifso, no changes have been made IF ucDisabled THEN ReclaimableRef MISC TRAP with trapParam = 2 Software implementation of the MCRECLAIMABLEREF microcode subroutine This is called from RECLAIMEDREF and from RECLAIMABLEREF This may pageFault or raise ZCTFull; ifso, no changes have been made Software implementation of the FoundInFHSnapshot microcode subroutine This is called only from MCRECLAIMABLEREF Software implementation of the FOSTableHash microcode subroutine r is all but the least significant 13 bits The hash function works as follows: assume the address of the REF is addr, and mask is the residue mask stored in the ZCT. The residue is addr[7..18]. The hash bucket is the low 12 bits of "(residue and mask) XOR addr[19..30]". This works for addresses up to 25 bits in length, and can be easily extended if this is a constraint. Bit 31 is always zero (REFs are always even word alligned). The residue, mask, and the hash bucket uniquely defines the original address (since you know residue, you know addr[7..18]; now compute "(residue and mask) xor hash bucket" to get bits 19-30). Since we never use a page of VM in the first 64 pages of memory for collectable storage, the residue is never zero. The purpose of the residueMask is to change the set of REFs that collide from hash to hash. residueMask is changed at the start of an incremental collection. Note that if the mask was xor'ed in instead of being and'ed, then the location of the collisions, but not the REFs that colliside, would change from hash to hash. Software implementation of the ReclaimedRef MISC This might pageFault or raise ZCTFull, RCUnderflowOccurred or LookFurtherAtReclaimedRef This is the only proc that raises LookFurtherAtReclaimedRef explicitly If this proc pageFaults or raises a signal then no changes have been made. IF ucDisabled THEN ReclaimedRef MISC TRAP with trapParam = 2 Software implementation of the CheckForRCUnderflow microcode subroutine This is called only from ASSIGNREF and from RECLAIMEDREF i.e. XXX TRAP with trapParam = 5 i.e. XXX TRAP with trapParam = 3 Software implementation of the DisableMicrocode MISC. In addition to "disabling" rc microcode, this guy should cause the store of ucode registers back into memory (zct^). cause rc operations to trap (except EnableMicrocode) Software implementation of the EnableMicrocode MISC In addition to "enabling" rc microcode, this guy should load ucode registers from memory (zct^). Κ ˜Jšœ™JšœA™AJšœ%™%Jšœ(™(J˜šΟk ˜ Jšœ œ\˜kJšœ œ˜'Jš œœœœœœ˜AJšœ œ˜Jšœœœ˜Jšœœ˜Jšœ œ˜"Jšœœp˜yJ˜—šœ˜JšœE™E—Jšœ$Οc˜7Jšœ˜J˜Jšœœœœ˜'J˜J™šœ™Jšœ œœ˜Jšœ8™8—J˜J™šœ™Jšœ œœ˜(Jšœ œœ˜)Jšœ œœ˜/Jšœ  œœ˜Jšœ œœ˜)—J˜J™šœ ™ ˜Jšœ,™,Jšœ:™:Jšœ0™0Jšž"œž!™V—š Πbkœœœž œœ˜WJšœ œ˜Jšœ8™8šœ˜šœ˜Jšœ*˜*Jšœ)˜)Jšœ˜šœœœœ˜-Jšœ%™%—Jšœ˜Jšœ˜J™JšœH™HJšœ žB˜MJ™Jšœ™Jšœ'˜'Jšœœž˜-Jšœ˜Jšœ)˜)J˜——Jšœ˜J˜Jšœ(™(Jšœ™Jšž.™.—š Ÿ œœœœ œœ˜VJšœ8™8Jšœ)˜)šœ˜šœ˜Jšœœ˜+Jšœœœ%œ˜VJšœ'ž˜AJšœ'˜'Jšœ˜J˜——Jšœ˜J˜JšœA™AJšœP™PJ™C—šŸ œœ œœœœœœœ˜KJšœœ˜ Jšœ˜Jšœ˜Jšœ1™1Jšœ™Jšœ˜šœœœ˜Jšœ]™]Jšœœœž<˜XJšœ$ž ˜1Jšœ˜šœœ œœ˜6Jšœ#™#Jšœ#™#JšœA™A—Jšœ˜—šœ œœ˜Jšœ/˜/Jšœ˜Jšœž"˜=š œœ œœœ ž˜gJšœA™A—šœ)žœ˜CJšœ!™!—Jšœ˜Jšœ˜—JšœB™Bšœœœ˜Jšœž$˜>Jšœœ˜Jšœ˜—Jšœ ž˜Jšœž˜—™Jšœ7™7JšœG™Gšœ™Jšœ™Jšœ™Jšœ™——šΟnœœ œœ˜1Jšœœœœ ˜Jšœ œœ˜Jšœ ˜ šœ ž˜JšœV™V—š œœœœœ˜PJšœœœœ˜&šœž=˜Dš œœœœœž˜@Jšœœœœ˜&Jšœœ ž#˜6šœ ˜Jšœ!™!——J˜—Jš œœœœž˜=—Jšœ œž˜(Jšœ˜J™Jšœ-™-—šŸ œœ œœ˜6Jšœ9™9Jš œ œœœœ˜BJšœ žB˜MJšœ)˜)Jšœ˜—˜Jšœ2™2Jšœžœ!™D—šŸœœ œœ˜7Jšœ˜Jšœ>™>Jšœœœ˜%Jšœ˜J™JšœD™DJšœ8™8Jšœžœ!™D—šŸœœœ˜8Jšœ˜&Jšœ˜Jšœ œž˜4Jš œœ œœœž˜Jšœœ˜-Jšœ ž3˜BJšœœœ œ ˜3—Jšœ˜™JšœE™EJšœ)™)—š œœ˜&Jšœœœ˜ Iprocšœ˜Kšœ ˜ šœ œœœ˜(Kš œœœ œœœ˜@—Kšœ%˜%Kšœ˜KšœΟsœ˜/Jšœ˜—˜Jšœ@™@—š  œœœžœ˜7Kšœ+œ˜>Kš œœœœœœ˜1Kšœ˜šœœœœ˜AKšœ*™*—Kšœœ˜šœœœœ)˜CI šœΤ‘œ™ζLšœ~‘œœ™—Kšœ˜—J™Jšœ0™0JšœW™WJšœF™FJšœJ™J—šŸ œœœœœœœœ˜QJšœ+˜+Jšœ<™