<> <> <> <> <> DIRECTORY Process USING [Detach, MsecToTicks, priorityFaultHandlers, SetPriority, Ticks, Yield], VM USING [AddressFault, CantDoIO, Clean, Interval, IOErrorType, LaundryError, PageCount, PageNumber, PageNumberForAddress, Pin], VMInternal USING [freePages, LaundryMode, GetCleaningCandidate, HasBackingStorage, PageCount, RealPageNumber, swapBufferSize], VMSideDoor USING [rmPages], VMStatistics USING [rmNewClean, rmOldClean]; VMLaundryImpl: MONITOR IMPORTS Process, VM, VMInternal, VMSideDoor, VMStatistics EXPORTS VM, VMInternal, VMStatistics = BEGIN OPEN VMInternal; <> <<>> normalHighMark: NAT = MIN[VMSideDoor.rmPages/10, 150]; bigHighMark: NAT _ MIN[VMSideDoor.rmPages/5, 400]; lowMark: NAT = normalHighMark/3; highMark: NAT _ bigHighMark; samplingInterval: Process.Ticks = Process.MsecToTicks[250]; <<>> <> dirtyVictims: BOOL; newVictims: BOOL; laundry: CONDITION _ [timeout: samplingInterval]; LastLaundryErrorRecord: VM.LaundryError _ [software, 0]; errorsNoted: INT _ 0; <> laundryWakeups, panicLaundryWakeups, uselessLaundryWakeups: PUBLIC INT _ 0; pagesCleaned, pagesCleanedPanic: PUBLIC INT _ 0; laundryCleanCalls: PUBLIC INT _ 0; <> NoteDirtyVictim: PUBLIC ENTRY PROC = { dirtyVictims _ TRUE; NOTIFY laundry; }; NoteNewVictim: PUBLIC ENTRY PROC = { newVictims _ TRUE; NOTIFY laundry; }; <> ForceCleaning: PUBLIC ENTRY SAFE PROC = CHECKED { newVictims _ TRUE; NOTIFY laundry; }; <> LaundryProcess: PROC = { <> prevCleanUsed: INT _ uselessLaundryWakeups ; oldRMNewClean: INT _ VMStatistics.rmNewClean; <> attitude: LaundryMode _ aggressive ; prevAttitude: LaundryMode _ casual ; prevMin: PageCount _ 1 ; Wait: ENTRY PROC RETURNS [LaundryMode] = { WaitReturn: PROC [mode: LaundryMode] RETURNS [LaundryMode]= { dirtyVictims _ FALSE; newVictims _ FALSE ; oldRMNewClean _ VMStatistics.rmNewClean; RETURN[mode]; }; IF dirtyVictims THEN RETURN[WaitReturn[panic]]; IF oldRMNewClean # VMStatistics.rmNewClean OR newVictims THEN RETURN[WaitReturn[aggressive]]; dirtyVictims _ FALSE; DO WAIT laundry; IF HasBackingStorage[0] THEN EXIT; ENDLOOP; IF dirtyVictims THEN RETURN[WaitReturn[panic]]; IF oldRMNewClean # VMStatistics.rmNewClean OR newVictims THEN RETURN[WaitReturn[aggressive]]; RETURN[casual]; }; <> Process.SetPriority[Process.priorityFaultHandlers]; DO cleanUsed: INT = VMStatistics.rmOldClean + VMStatistics.rmNewClean ; cleanedThisTime: INT _ 0; totalCleanSkipped: PageCount ; startPasses: INT _ -1 ; startRover: RealPageNumber _ 0; minCleaning: PageCount _ 1; knownFreeOrClean: INT _ lowMark; -- best start guess --*stats*-- laundryWakeups _ laundryWakeups.SUCC; IF attitude = panic THEN panicLaundryWakeups _ panicLaundryWakeups.SUCC; totalCleanSkipped _ 0 ; IF attitude # casual THEN { highMark _bigHighMark ; minCleaning _ IF prevAttitude = casual THEN 10 ELSE IF prevMin = 10 THEN 4 ELSE 1 ; }; IF attitude # casual OR (cleanUsed - prevCleanUsed) >= (highMark - lowMark) THEN { UNTIL cleanedThisTime >= highMark DO interval: VM.Interval; cleanSkipped: PageCount ; passes: INT ; rover: RealPageNumber ; [interval: interval, cleanSkipped: cleanSkipped, passes: passes, rover: rover] _ GetCleaningCandidate[ desired: swapBufferSize, -- larger run than this means multiple transfers in Clean comfortLevel: lowMark + cleanedThisTime, tryHard: attitude ]; IF startPasses < 0 THEN { startPasses _ passes; startRover _ rover;}; totalCleanSkipped _ totalCleanSkipped + cleanSkipped ; IF interval.count = 0 THEN <> EXIT; IF interval.count >= minCleaning THEN { VM.Clean[interval ! VM.AddressFault => { interval.count _ VM.PageNumberForAddress[address] - interval.page; CONTINUE }; VM.CantDoIO => { <> interval.count _ page - interval.page; VM.Pin[ [page,1] ]; ReportError[reason, page]; CONTINUE }; ]; cleanedThisTime _ cleanedThisTime + interval.count; <<*stats*-->> laundryCleanCalls _ laundryCleanCalls.SUCC; }; knownFreeOrClean _ freePages + cleanedThisTime + totalCleanSkipped - (VMStatistics.rmOldClean + VMStatistics.rmNewClean - cleanUsed) ; <> IF ((attitude = casual) AND (knownFreeOrClean >= highMark)) THEN EXIT; IF (passes > startPasses + 1) OR (passes > startPasses AND rover <= startRover) THEN EXIT; <> Process.Yield[]; ENDLOOP; prevCleanUsed _ VMStatistics.rmOldClean + VMStatistics.rmNewClean; }; <<*stats*-->> IF cleanedThisTime = 0 THEN uselessLaundryWakeups _ uselessLaundryWakeups.SUCC ELSE { pagesCleaned _ pagesCleaned + cleanedThisTime; IF attitude = panic THEN pagesCleanedPanic _ pagesCleanedPanic + cleanedThisTime; }; prevAttitude _ attitude; prevMin _ minCleaning ; IF knownFreeOrClean < lowMark AND cleanedThisTime >= highMark THEN attitude _ aggressive ELSE attitude _ Wait[]; highMark _ normalHighMark ; ENDLOOP; }; ReportError: ENTRY PROC [ errorType: VM.IOErrorType, page: VM.PageNumber ] = { LastLaundryErrorRecord _ [errorType,page] ; errorsNoted _ errorsNoted + 1; }; LastLaundryError: PUBLIC ENTRY SAFE PROC RETURNS [VM.LaundryError] = CHECKED { RETURN[LastLaundryErrorRecord]; }; Process.Detach[FORK LaundryProcess[]]; END. <> <> <> <<>> <<>> <<>> <<>> <<>>