ComputeServerMaintenanceImpl.mesa
The Compute Server Statistics program running on the server that informs the controller of its current state.
Last Edited by: Bob Hagmann, August 17, 1986 2:09:36 pm PDT
Copyright © 1984 by Xerox Corporation. All rights reserved.
DIRECTORY
AMBridge,
AMModel,
AMProcess,
AMTypes,
Basics,
BasicTime,
CedarProcess,
Commander,
CommandTool,
ComputeServer,
ComputeServerControllerRpcControl,
ComputeServerInternal,
ComputeServerRpcControl,
ComputeServerControl,
ComputeServerServer,
ComputeServerStatistics,
FSBackdoor,
FSFileOps,
Idle,
Interpreter,
IO,
Loader,
LupineRuntime,
MessageWindow,
PrincOps,
PrincOpsUtils,
PrintTV,
Process,
ProcessBackdoor,
Rope,
RPC,
SafeStorage,
SummonerControllerControl,
SymTab,
SystemVersion,
Terminal,
UserCredentials,
UserProfile,
ViewerClasses,
ViewerIO,
ViewerOps,
VMSideDoor,
VMStatistics,
WatchStats,
WorldVM;
ComputeServerMaintenanceImpl: CEDAR MONITOR
IMPORTS AMBridge, AMModel, AMProcess, AMTypes, Basics, BasicTime, CedarProcess, Commander, CommandTool, ComputeServerControllerRpcControl, ComputeServerInternal, ComputeServerRpcControl, ComputeServerServer, ComputeServerStatistics, FSBackdoor, FSFileOps, Idle, Interpreter, IO, Loader, LupineRuntime, MessageWindow, PrincOpsUtils, Process, ProcessBackdoor, Rope, RPC, SafeStorage, SummonerControllerControl, SymTab, SystemVersion, UserCredentials, UserProfile, ViewerIO, ViewerOps, VMSideDoor, VMStatistics, WatchStats, WorldVM
EXPORTS ComputeServerControl, ComputeServerInternal, ComputeServerStatistics
SHARES ComputeServerServer
= BEGIN
Variable Declarations
All code invoked by this process should be resident
ROPE: TYPE = Rope.ROPE;
TimeUpThisIncarnation: PUBLIC INT ← 0;
RequestesThisIncarnation: PUBLIC INT ← 0;
TimeBusyThisIncarnation: PUBLIC INT ← 0;
TotalTimeUp: PUBLIC INT ← 0;
TotalRequestes: PUBLIC INT ← 0;
TotalTimeBusy: PUBLIC INT ← 0 ;
watchStatsRecord: WatchStats.WatchStatsRecord;
lastNoGFI: INT ← -1;
noGFIChanged: BOOLTRUE;
LastRMReclamations: INT;
LastTime: BasicTime.GMT ;
serverMachineName: RPC.ShortROPE;
serverMachinePupAddress: RPC.ShortROPE;
wasUp: BOOLFALSE;
isIdle: BOOLFALSE; -- is Cedar "idle" or not
aConditionSomewhere: LONG POINTER TO CONDITION ← NIL;
usingVerticalRetrace: BOOLTRUE;
verticalRetraceTV: AMTypes.TV;
pointerToVerticalRetraceTV: LONG POINTER TO CONDITION ← NIL;
myConditionForCPUStats: CONDITION;
notifyCPUStatsProcess: LONG POINTER TO PrincOps.ProcessStateBlock ← NIL;
PackageEntry: TYPE = ComputeServerInternal.PackageEntry;
CmdEntryObject: TYPE = ComputeServerInternal.CmdEntryObject;
CmdEntry: TYPE = ComputeServerInternal.CmdEntry;
ServiceEnabled: PUBLIC BOOLFALSE;
IdleTransitionsEnabled: PUBLIC BOOLFALSE;
whatsActiveProcessDead: CONDITION;
whatsActiveProcess: PROCESSNIL;
active: BOOLFALSE;
enabled: BOOLFALSE;
levelCounters: ARRAY PrincOps.Priority OF INT;
backgroundSeen: INT ← 0;
timeOutType: TYPE = ARRAY [0..maxIndex) OF RECORD[process: PrincOps.PsbIndex, gfh: PrincOps.GlobalFrameHandle, pc: CARDINAL];
timeout: REF timeOutType;
maxIndex: CARDINAL = 50;
index: CARDINAL ← 0;
TerminalImplGF: AMTypes.TV;
searchReadyList: BOOLEANTRUE; -- sometimes the user gets the machine gets into a state where searching the ready list is a bad idea. This boolean allows the user to stop the Spy from searching the ready list.
maxVerticalRetraceProcessIndex: CARDINAL = 5;
verticalRetraceProcesses: ARRAY [0..maxVerticalRetraceProcessIndex) OF RECORD[
psbi: PrincOps.PsbIndex,
seenCounter: INT, -- ranges from 0 through 10000; bumped when seen, debumped when not seen
seenThisTime: BOOL
];
nextVerticalRetraceProcesses: CARDINAL ← 0;
verticalRetraceProcessCount: INT ← 0;
maxVerticalRetraceProcessCount: INT = 5;
vrpLockSet: BOOLFALSE;
vrpLockAvailable: CONDITION ← [timeout: 0];
triedToPowerOff: BOOLFALSE;
Server Startup and Shutdown
ServerOn: Commander.CommandProc = {
argv: CommandTool.ArgumentVector ← NIL;
controllerName, remoteCommandDirectory, localCommandDirectory: Rope.ROPENIL;
IF ServiceEnabled THEN RETURN ["Server already enabled"];
argv ← CommandTool.Parse[cmd ! CommandTool.Failed => { msg ← errorMsg; CONTINUE; }];
IF argv = NIL THEN RETURN[$Failure, msg];
IF argv.argc > 1 THEN controllerName ← argv[1];
IF argv.argc > 2 THEN remoteCommandDirectory ← argv[2];
IF argv.argc > 3 THEN localCommandDirectory ← argv[3];
SummonerControllerControl.StartUpController[controllerName];
msg ← StartUpServer[controllerName, remoteCommandDirectory, localCommandDirectory];
IF ~Rope.IsEmpty[msg] THEN SummonerControllerControl.ShutDownController[];
};
StartUpServer: PUBLIC PROC [controllerName: Rope.ROPENIL, remoteCommandDirectory: Rope.ROPENIL, localCommandDirectory: Rope.ROPENIL] RETURNS [msg: ROPENIL] = {
name, password: ROPE;
ComputeServerInternal.LastActiveTime ← BasicTime.Now[];
[name, password] ← UserCredentials.Get[];
msg ← ComputeServerInternal.InitCommands[remoteCommandDirectory, localCommandDirectory];
IF ~Rope.IsEmpty[msg] THEN RETURN;
ServiceEnabled ← TRUE;
ComputeServerInternal.ReportServerEvent[type: serverEnabled, command: NIL, startTime: BasicTime.nullGMT, endTime: BasicTime.nullGMT, remoteMachineName: NIL, userName: NIL];
TRUSTED {Process.Detach[FORK TickStatistics[]]; };
ComputeServerInternal.ControllerGVName ← IF Rope.IsEmpty[controllerName] THEN UserProfile.Token["Summoner.ControllerName"] ELSE controllerName;
IF ComputeServerInternal.ControllerGVName = NIL THEN ComputeServerInternal.ControllerGVName ← "PaloAlto1.summoner" ;
ComputeServerRpcControl.ExportInterface[
interfaceName: [ type: "ComputeServer.summoner", instance: ComputeServerInternal.MyNetAddressRope, version: [1,1]],
user: name,
password: RPC.MakeKey[password]
! LupineRuntime.BindingError => CONTINUE;];
ComputeServerInternal.ControllerInterface ← ComputeServerControllerRpcControl.ImportNewInterface[
interfaceName: [ type: "ComputeServerController.summoner", instance: ComputeServerInternal.ControllerGVName, version: [1,1]]
! RPC.ImportFailed => {
SummonerControllerControl.ControllerCannnotBeImported[why];
CONTINUE;
};
];
IF ComputeServerInternal.ControllerInterface # NIL THEN {
SummonerControllerControl.NoticeControllerIsUp[];
TellControllerAboutExtraCommands[];
};
ComputeServerInternal.InitStats[ComputeServerInternal.myHostName, ComputeServerInternal.MyNetAddressRope];
TRUSTED {Process.Detach[FORK ComputeServerInternal.SummonerServerMonitor[]]; };
StartCounting[];
};
StartCounting: PUBLIC ENTRY PROC = {
priority: CedarProcess.Priority;
enabled ← TRUE;
WHILE whatsActiveProcess # NIL AND ~active DO WAIT whatsActiveProcessDead; ENDLOOP;
IF ~active THEN {
priority ← CedarProcess.GetPriority[];
CedarProcess.SetPriority[realTime];
TRUSTED {Process.Detach[whatsActiveProcess ← FORK WhatsActive[]]; };
CedarProcess.SetPriority[priority];
};
};
StopCounting: PUBLIC ENTRY PROC = {
IF ~active THEN RETURN; -- already stopped
enabled ← FALSE;
};
WhatsActive: PROC = TRUSTED {
topProcess: PrincOps.PsbHandle;
lastTopProcess: PrincOps.PsbHandle;
noBackgroundProcesses: INT ← 0;
done: BOOLFALSE;
topLevel: Process.Priority ← Process.priorityIdleProcess;
sameProcessInARow: INT ← 0;
frame: PrincOps.FrameHandle;
myPrincOps: PrincOps.PsbHandle = PrincOpsUtils.ReadPSB[];
handleMask: PrincOps.PsbLink = [
failed: FALSE, priority: 0, next: LAST[PrincOps.PsbIndex], reserved: 0, vector: FALSE];
NextHandle: PROC [link: CARDINAL] RETURNS [PrincOps.PsbHandle] = TRUSTED INLINE {
RETURN[LOOPHOLE[Basics.BITAND[link, LOOPHOLE[handleMask]]]]};
SearchReadyList: PROC = TRUSTED INLINE {
once: BOOLEANFALSE;
foundTop: BOOLEANFALSE;
headOfReadyList, current: PrincOps.PsbHandle;
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
verticalRetraceProcesses[p].seenThisTime ← FALSE;
ENDLOOP;
noBackgroundProcesses ← 0;
PrincOpsUtils.DisableInterrupts[];
topLevel ← Process.priorityIdleProcess;
headOfReadyList ← NextHandle[LOOPHOLE[PrincOps.PDA.ready]];
headOfReadyList ← NextHandle[LOOPHOLE[PrincOps.PDA[headOfReadyList].link]]; -- want the SECOND psb.
FOR current ← headOfReadyList, NextHandle[LOOPHOLE[PrincOps.PDA[current].link]] DO
psb: LONG POINTER TO PrincOps.ProcessStateBlock = @PrincOps.PDA[current];
link: PrincOps.PsbLink = psb.link;
level: Process.Priority = link.priority;
skip: BOOLEANFALSE;
IF current = headOfReadyList THEN IF once THEN EXIT ELSE once ← TRUE;
IF psb = notifyCPUStatsProcess THEN LOOP;
IF level = Process.priorityIdleProcess THEN LOOP;
IF level = Process.priorityBackground THEN noBackgroundProcesses ← noBackgroundProcesses + 1;
IF foundTop THEN LOOP;
IF current = myPrincOps THEN LOOP;
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
skipPsb: LONG POINTER TO PrincOps.ProcessStateBlock = @PrincOps.PDA.block[verticalRetraceProcesses[p].psbi];
IF skipPsb = psb THEN {
skip ← TRUE;
IF level > Process.priorityForeground THEN {
IF verticalRetraceProcesses[p].seenCounter < 10000 THEN verticalRetraceProcesses[p].seenCounter ← verticalRetraceProcesses[p].seenCounter + 1;
verticalRetraceProcesses[p].seenThisTime ← TRUE;
};
EXIT;
};
ENDLOOP;
IF skip THEN LOOP;
IF ~link.vector AND PrincOps.PDA.state[level] = PrincOps.NullStateVectorHandle THEN LOOP; -- no SV.
frame ← IF link.vector THEN PrincOps.PDA[psb.context.state].frame ELSE psb.context.frame;
skip over a process that appears in the ready queue because it just timed out.
(The Spy wakes up with all of the other timeouts. Since it is the highest priority,
it will run first. All of the other timeouts will appear on the ready list. Most likely,
they will just check some condition and then go back to sleep. This will mask the
more interesting processes.)
FOR i: CARDINAL IN [0..index) DO
IF timeout[i].gfh = NIL AND timeout[i].pc = 0 THEN EXIT;
IF timeout[i].gfh # frame.accesslink OR timeout[i].pc # frame.pc THEN LOOP;
skip ← TRUE; EXIT;
ENDLOOP;
IF skip THEN LOOP;
we have a good process!
topLevel ← level;
topProcess ← current;
foundTop ← TRUE;
ENDLOOP;
PrincOpsUtils.EnableInterrupts[];
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
IF verticalRetraceProcesses[p].seenThisTime = FALSE THEN verticalRetraceProcesses[p].seenCounter ← MAX[0, verticalRetraceProcesses[p].seenCounter - 1];
ENDLOOP;
};
MAIN LOOP
FOR p: PrincOps.Priority IN PrincOps.Priority DO
levelCounters[p] ← 0;
ENDLOOP;
Get our hands on the verticalRetrace CONDITION in TerminalImpl. This gets a BROADCAST every vertical retrace, if we are in idle or not.
IF verticalRetraceTV = NIL THEN {
[] ← NotifyProcessDead[];
RETURN;
};
active ← TRUE;
aConditionSomewhere ← pointerToVerticalRetraceTV;
usingVerticalRetrace ← TRUE;
WHILE ~done DO
ticksToRebuildSetTimeouts: INT ← 0;
rebuildCounter: INT ← 0;
DO
waitForBWVerticalRetrace: ENTRY PROC = TRUSTED {
WAIT aConditionSomewhere;
};
Used to be Terminal.WaitForBWVerticalRetrace, but that does not tick when the machine is idle
waitForBWVerticalRetrace[];
IF ~enabled THEN EXIT;
IF ticksToRebuildSetTimeouts MOD 3 = 0 THEN {
IF searchReadyList THEN SearchReadyList[];
levelCounters[topLevel] ← levelCounters[topLevel] + 1;
backgroundSeen ← backgroundSeen + noBackgroundProcesses;
IF lastTopProcess = topProcess THEN {
sameProcessInARow ← sameProcessInARow + 1;
IF topLevel > Process.priorityForeground AND sameProcessInARow > 512 THEN {
psbi: INT;
alreadySkipped: BOOLFALSE;
psbi ← (topProcess - PrincOps.NullPsbHandle)/SIZE[PrincOps.ProcessStateBlock];
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
IF verticalRetraceProcesses[p].psbi = psbi THEN {
alreadySkipped ← TRUE;
EXIT;
};
ENDLOOP;
IF ~alreadySkipped THEN {
Process.Detach[FORK AddToVerticalRetraceProcesses[topProcess]];
};
};
}
ELSE {
sameProcessInARow ← 0;
lastTopProcess ← topProcess;
};
};
ticksToRebuildSetTimeouts ← ticksToRebuildSetTimeouts + 1;
IF ticksToRebuildSetTimeouts MOD 1024 = 0 THEN {
IF index = maxIndex OR (ticksToRebuildSetTimeouts ← ticksToRebuildSetTimeouts + 1) > 7000 THEN {
ticksToRebuildSetTimeouts ← 0;
rebuildCounter ← rebuildCounter + 1;
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
IF verticalRetraceProcesses[p].seenCounter <= 0 THEN {
reArrange: ENTRY PROC = TRUSTED {
nextVerticalRetraceProcesses ← nextVerticalRetraceProcesses - 1;
IF nextVerticalRetraceProcesses # p THEN {
verticalRetraceProcesses[p].psbi ← verticalRetraceProcesses[nextVerticalRetraceProcesses].psbi;
verticalRetraceProcesses[p].seenCounter ← verticalRetraceProcesses[nextVerticalRetraceProcesses].seenCounter;
};
verticalRetraceProcesses[nextVerticalRetraceProcesses].psbi ← 0;
verticalRetraceProcesses[nextVerticalRetraceProcesses].seenCounter ← 0;
};
reArrange[];
};
ENDLOOP;
IF rebuildCounter > 10 THEN {
SetTimeouts[TRUE];
rebuildCounter ← 0;
}
ELSE SetTimeouts[noGFIChanged];
noGFIChanged ← FALSE;
};
};
ENDLOOP;
done ← NotifyProcessDead[];
ENDLOOP;
Process.SetPriority[Process.priorityNormal];
};
NotifyCPUStats: PROC = {
cnt: INT ← 0 ;
TRUSTED {notifyCPUStatsProcess ← @PrincOps.PDA.block[(PrincOpsUtils.ReadPSB[] - PrincOps.NullPsbHandle)/SIZE[PrincOps.ProcessStateBlock]]; };
CedarProcess.SetPriority[realTime];
DO
doNotify: ENTRY PROC = {
NOTIFY myConditionForCPUStats;
};
IF usingVerticalRetrace THEN cnt ← cnt + 1 ELSE cnt ← 0;
IF cnt > 5 THEN EXIT;
doNotify[];
Process.Pause[4];
ENDLOOP;
notifyCPUStatsProcess ← NIL;
};
AddToVerticalRetraceProcesses: PROC [ topProcess: PrincOps.PsbHandle] = TRUSTED {
getLock: ENTRY PROC = TRUSTED {
verticalRetraceProcessCount ← verticalRetraceProcessCount + 1;
WHILE vrpLockSet DO WAIT vrpLockAvailable ENDLOOP;
vrpLockSet ← TRUE;
};
freeLock: ENTRY PROC = TRUSTED {
verticalRetraceProcessCount ← verticalRetraceProcessCount - 1;
vrpLockSet ← FALSE;
NOTIFY vrpLockAvailable;
};
localWorld: WorldVM.World;
rootContext: AMModel.Context;
psbi: INT;
stack: AMTypes.TV;
process: AMProcess.Process;
IF verticalRetraceProcessCount > maxVerticalRetraceProcessCount THEN RETURN;
CedarProcess.SetPriority[normal];
getLock[];
IF nextVerticalRetraceProcesses >= maxVerticalRetraceProcessIndex THEN {
freeLock[];
RETURN;
};
localWorld ← WorldVM.LocalWorld[];
rootContext ← AMModel.RootContext[localWorld];
psbi ← (topProcess - PrincOps.NullPsbHandle)/SIZE[PrincOps.ProcessStateBlock];
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
IF verticalRetraceProcesses[p].psbi = psbi THEN {
freeLock[];
RETURN;
};
ENDLOOP;
process ← AMProcess.PSBIToTV[localWorld, psbi];
AMProcess.Freeze[LIST[process], LIST[rootContext]];
AMProcess.Adjust[LIST[process], LIST[rootContext]];
[stack: stack] ← AMProcess.GetState[process];
{
IF stack # NIL THEN {
str: IO.STREAM = IO.ROS[];
gf: AMTypes.TV;
gf ← AMTypes.GlobalParent[stack];
PrintTV.Print[stack, str];
AMProcess.Thaw[LIST[process]];
IF AMTypes.TVEq[gf, TerminalImplGF ! AMTypes.Error => GOTO bailout] THEN {
IF Rope.Run["TerminalImpl.InternalWaitForBWVerticalRetrace", 0, IO.RopeFromROS[str], 0, FALSE] > 40 THEN {
insert: ENTRY PROC = TRUSTED {
recheck under monitor to ensure unique entries and no overflow
IF nextVerticalRetraceProcesses >= maxVerticalRetraceProcessIndex THEN {
freeLock[];
RETURN;
};
FOR p: CARDINAL IN [0..nextVerticalRetraceProcesses) DO
IF verticalRetraceProcesses[p].psbi = psbi THEN {
freeLock[];
RETURN;
};
ENDLOOP;
verticalRetraceProcesses[nextVerticalRetraceProcesses].psbi ← psbi;
verticalRetraceProcesses[nextVerticalRetraceProcesses].seenCounter ← 10000;
nextVerticalRetraceProcesses ← nextVerticalRetraceProcesses + 1;
};
insert[];
};
} ELSE AMProcess.Thaw[LIST[process]];
freeLock[];
EXITS
bailout => freeLock[];
};
};
NotifyProcessDead: ENTRY PROC RETURNS[exit: BOOLTRUE] = INLINE {
IF enabled THEN RETURN [FALSE]
ELSE {
NOTIFY whatsActiveProcessDead;
active ← FALSE;
whatsActiveProcess ← NIL;
};
};
ServerOff: Commander.CommandProc = {
ShutDownServer[];
SummonerControllerControl.ShutDownController[];
};
ShutDownServer: PUBLIC PROC [] RETURNS [] = {
ComputeServerInternal.ControllerInterface ← NIL ;
ServiceEnabled ← FALSE;
ComputeServerInternal.ReportServerEvent[type: serverDisabled, command: NIL, startTime: BasicTime.nullGMT, endTime: BasicTime.nullGMT, remoteMachineName: NIL, userName: NIL];
ComputeServerInternal.PackageTable ← SymTab.Create[mod: 59, case: FALSE];
ComputeServerInternal.ConfigTable ← SymTab.Create[mod: 59, case: FALSE];
StopCounting[];
};
DoStartup: PROC = {
msg: ROPE;
SummonerControllerControl.StartUpController[ComputeServerInternal.ControllerGVName];
msg ← StartUpServer[ComputeServerInternal.ControllerGVName, ComputeServerInternal.RemoteCommandDir, ComputeServerInternal.LocalCommandDir];
IF ~Rope.IsEmpty[msg] THEN {
SummonerControllerControl.ShutDownController[];
MessageWindow.Append[message: Rope.Concat["Can't Start Summoner Server because ", msg], clearFirst: TRUE];
};
};
IdleTransition: Idle.IdleHandler = {
PROC [data: REF, reason: IdleReason]
SELECT reason FROM
becomingBusy => {
isIdle ← FALSE;
};
becomingIdle => {
isIdle ← TRUE;
};
ENDCASE;
IF IdleTransitionsEnabled THEN {
SELECT reason FROM
becomingBusy => {
ShutDownServer[];
SummonerControllerControl.ShutDownController[];
};
becomingIdle => {
TRUSTED {Process.Detach[FORK DoStartup[]];};
};
ENDCASE;
};
};
EnableAutoIdle: Commander.CommandProc = {
IdleTransitionsEnabled ← TRUE;
};
DisableAutoIdle: Commander.CommandProc = {
IdleTransitionsEnabled ← FALSE;
};
Issue Statistics
SendStats: PUBLIC PROC [] RETURNS [success, terminateService, newPackage: BOOLFALSE, queueingCommands: LIST OF Rope.ROPE] = {
May Raise RPC.CallFailed
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord;
numberCPUs: CARDINAL ← 1;
diskPartionSize: INT;
freePagesOnDisk: INT;
freeboard: INT;
oldestLRUFileDate: BasicTime.GMT;
freeProcesses: CARDINAL ← 0;
totalSamples: INT ← 0;
idleBackGroundSamples: INT ← 0;
nonBackgroundCPULoad: REAL;
aveBackgroundLoad: REAL;
NowRMReclamations: INT;
ReclamationRate: REAL;
NowTime: BasicTime.GMT = BasicTime.Now[];
TimePeriod: REAL;
firstCall: BOOL;
[diskPartionSize, freePagesOnDisk, freeboard] ← FSBackdoor.VolumePages[];
Get values for freeGFI, freeMDS, freeVM, and CPULoad
watchStatsRecord ← WatchStats.GetWatchStats[];
IF watchStatsRecord.gfiFree # lastNoGFI THEN {
noGFIChanged ← TRUE;
lastNoGFI ← watchStatsRecord.gfiFree;
};
NowRMReclamations ← VMStatistics.rmReclamations;
TimePeriod ← BasicTime.Period[LastTime, NowTime];
TimePeriod ← MAX[1.0, TimePeriod];
IF LastRMReclamations = 0 THEN ReclamationRate ← -1.0
ELSE ReclamationRate ← (NowRMReclamations - LastRMReclamations) / TimePeriod ;
LastRMReclamations ← NowRMReclamations;
oldestLRUFileDate ← FSFileOps.OldestLruDate[];
IF FSFileSpaceImpl.lru.first = NIL THEN oldestLRUFileDate ← BasicTime.nullGMT
ELSE oldestLRUFileDate ← FSFileSpaceImpl.lru.first.used;
freeProcesses ← ProcessBackdoor.CountFreeProcesses[];
FOR p: PrincOps.Priority IN PrincOps.Priority DO
delta: INT = levelCounters[p];
levelCounters[p] ← 0;
totalSamples ← totalSamples + delta;
IF p <= Process.priorityBackground THEN idleBackGroundSamples ← idleBackGroundSamples + delta;
ENDLOOP;
SELECT TRUE FROM
~isIdle AND ~usingVerticalRetrace => {
Get our hands on the bwRetrace CONDITION in TerminalImpl. This gets a BROADCAST every vertical retrace, if we are not idle.
IF verticalRetraceTV # NIL THEN TRUSTED {
aConditionSomewhere ← pointerToVerticalRetraceTV;
usingVerticalRetrace ← TRUE;
};
};
totalSamples = 0 AND usingVerticalRetrace AND isIdle => TRUSTED {
This gets a BROADCAST every vertical retrace, if we are in idle or not.
doBroadcast: ENTRY PROC = TRUSTED INLINE {
IF oldCondition # NIL THEN BROADCAST oldCondition^;
};
oldCondition: LONG POINTER TO CONDITION;
oldCondition ← aConditionSomewhere;
aConditionSomewhere ← @myConditionForCPUStats;
usingVerticalRetrace ← FALSE;
doBroadcast[];
Process.Detach[FORK NotifyCPUStats[]];
};
ENDCASE;
IF totalSamples = 0 THEN {
nonBackgroundCPULoad ← watchStatsRecord.cpuLoad;
aveBackgroundLoad ← -1.0;
}
ELSE {
nonBackgroundCPULoad ← MIN[1.0 - ((1.0*idleBackGroundSamples)/totalSamples), watchStatsRecord.cpuLoad];
aveBackgroundLoad ← (1.0 * backgroundSeen) / totalSamples;
};
backgroundSeen ← 0;
firstCall ← ~wasUp AND ServiceEnabled;
controllerInterface ← ComputeServerInternal.ControllerInterface;
IF controllerInterface # NIL THEN [terminateService, newPackage, queueingCommands] ← controllerInterface.NewStats[serverMachineName: serverMachineName, serverMachinePupAddress: serverMachinePupAddress, serverUP: ServiceEnabled, firstCall: firstCall, machineType: SystemVersion.machineType, mainMemory: VMSideDoor.rmPages, numberCPUs: numberCPUs, diskPartionSize: diskPartionSize, freePagesOnDisk: freePagesOnDisk, freeboard: freeboard, freeGFI: watchStatsRecord.gfiFree, freeMDS: watchStatsRecord.mdsFree, freeVM: watchStatsRecord.vmFree, oldestLRUFileDate: oldestLRUFileDate, CPULoad: watchStatsRecord.cpuLoad, nonBackgroundCPULoad: nonBackgroundCPULoad, reclamationRate: ReclamationRate, freeProcesses: freeProcesses, userName: UserCredentials.Get[].name, currentRequests: ComputeServerInternal.CurrentRequests, aveBackgroundLoad: aveBackgroundLoad ];
success ← TRUE;
wasUp ← ServiceEnabled;
};
Server Monitor
SummonerServerMonitor: PUBLIC PROC [] RETURNS [] = {
delayToNextStat: INT ← 2;
WHILE ServiceEnabled DO
success: BOOLTRUE;
terminateService: BOOLFALSE;
newPackage: BOOLFALSE;
queueingCommands: LIST OF ROPE;
cycleCount: INT ← 0;
tryDifferentController: BOOLFALSE;
WHILE ~tryDifferentController AND ComputeServerInternal.ControllerInterface # NIL AND success AND NOT terminateService AND ServiceEnabled DO
Process.Pause[Process.SecondsToTicks[delayToNextStat]];
[success, terminateService, newPackage, queueingCommands] ← ComputeServerInternal.SendStats[
! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
newPackage ← FALSE;
queueingCommands ← NIL;
CONTINUE;
};
];
IF newPackage THEN {
ComputeServerInternal.getNewPackages[];
};
IF queueingCommands # NIL THEN newQueueingCommands[queueingCommands];
newPackage ← FALSE;
delayToNextStat ← 9;
IF (cycleCount ← cycleCount + 1) > 10 THEN {
cycleCount ← 0;
ComputeServerInternal.KillOldUnstartedServices[];
};
IF cycleCount = 1 OR cycleCount = 6 THEN {
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord ← ComputeServerInternal.ControllerInterface;
IF controllerInterface # NIL THEN {
[tryDifferentController: tryDifferentController] ← controllerInterface.GetSomeInfo[ ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
newPackage ← FALSE;
queueingCommands ← NIL;
CONTINUE;
};
];
};
};
IF ComputeServerInternal.DisableIFIdle AND ComputeServerInternal.ActiveServices = 0 THEN {
now: BasicTime.GMT = BasicTime.Now[];
unpack: BasicTime.Unpacked;
minuteTime: INT;
IF BasicTime.Period[from: ComputeServerInternal.LastActiveTime, to: now] > 3600 THEN {
unpack ← BasicTime.Unpack[now];
minuteTime ← unpack.hour*100+unpack.minute;
IF minuteTime > ComputeServerInternal.DisableIFIdleAfter OR minuteTime < ComputeServerInternal.DisableIFIdleBefore THEN {
triedToPowerOff ← TRUE;
ShutDownServer[];
SummonerControllerControl.ShutDownController[];
};
};
};
ENDLOOP;
Send final message that we are no longer a server
IF ~tryDifferentController AND ComputeServerInternal.ControllerInterface # NIL AND ~ServiceEnabled THEN [success, terminateService] ← ComputeServerInternal.SendStats[
! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
IF terminateService THEN {
ServiceEnabled ← FALSE;
ComputeServerInternal.ReportServerEvent[type: serverDisabled, command: NIL, startTime: BasicTime.nullGMT, endTime: BasicTime.nullGMT, remoteMachineName: NIL, userName: NIL];
};
IF ServiceEnabled AND (tryDifferentController OR ComputeServerInternal.ControllerInterface = NIL) THEN ComputeServerInternal.ControllerInterface ← ComputeServerControllerRpcControl.ImportNewInterface[
interfaceName: [ type: "ComputeServerController.summoner", instance: ComputeServerInternal.ControllerGVName, version: [1,1]]
! RPC.ImportFailed => {
SummonerControllerControl.ControllerCannnotBeImported[why];
CONTINUE;
};];
IF ComputeServerInternal.ControllerInterface # NIL THEN {
SummonerControllerControl.NoticeControllerIsUp[];
TellControllerAboutExtraCommands[];
};
ComputeServerInternal.InitStats[ComputeServerInternal.myHostName, ComputeServerInternal.MyNetAddressRope];
ComputeServerInternal.KillOldUnstartedServices[];
IF ComputeServerInternal.ControllerInterface = NIL THEN Process.Pause[Process.SecondsToTicks[7]];
ENDLOOP;
};
Queueing Maintence
newQueueingCommands: PROC [queueingCommands: LIST OF ROPE] = {
FOR q: LIST OF ROPE ← queueingCommands, q.rest UNTIL q = NIL DO
foundInCmdTable: BOOL;
cmdVal: REF ANY;
cmdEntry: CmdEntry;
[found: foundInCmdTable, val: cmdVal] ← SymTab.Fetch[x: ComputeServerInternal.CommandTable, key: q.first];
cmdEntry ← NARROW[cmdVal];
IF cmdEntry # NIL THEN {
foundPack: BOOL;
valPack: REF ANY;
packageEntry: PackageEntry;
cmdEntry.doQueueing ← TRUE;
[found: foundPack, val: valPack] ← SymTab.Fetch[x: ComputeServerInternal.PackageTable, key: cmdEntry.package];
IF ~foundPack THEN LOOP;
packageEntry ← NARROW[valPack, PackageEntry];
IF packageEntry.exclusive AND ComputeServerInternal.ActiveServices > 0 THEN LOOP;
IF packageEntry.nowActive < packageEntry.maxCountActive THEN {
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord;
controllerInterface ← ComputeServerInternal.ControllerInterface;
IF controllerInterface # NIL THEN {
controllerInterface.MightAcceptQueuedCommand[serverMachineAddress: ComputeServerInternal.MyNetAddressRope, commandName: q.first ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
cmdEntry.okToQueuePosted ← TRUE;
};
};
}
ELSE { -- might be an extra command - they aren't in the CommandTable
foundRegCmd: BOOL;
controllerInterface: ComputeServerControllerRpcControl.InterfaceRecord;
[found: foundRegCmd] ← SymTab.Fetch[x: ComputeServerServer.Registry, key: q.first];
IF foundRegCmd AND (controllerInterface ← ComputeServerInternal.ControllerInterface) # NIL THEN {
controllerInterface.MightAcceptQueuedCommand[serverMachineAddress: ComputeServerInternal.MyNetAddressRope, commandName: q.first ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
};
};
ENDLOOP;
};
New Controller Processing
TellControllerAboutExtraCommands: PROC = {
procData: LIST OF ComputeServerServer.RegisteredProcHandle;
EachPairProc: SymTab.EachPairAction = {
loopList: LIST OF ComputeServerInternal.RegisteredProcHandle;
procData ← NARROW[val];
FOR loopList ← procData, loopList.rest UNTIL loopList = NIL DO
procHandle: ComputeServerInternal.RegisteredProcHandle ← loopList.first;
foundCmdTab: BOOL;
tempControllerInterface: ComputeServerControllerRpcControl.InterfaceRecord ← NIL;
[found: foundCmdTab] ← SymTab.Fetch[x: ComputeServerInternal.CommandTable, key: procHandle.service];
IF ~foundCmdTab AND (tempControllerInterface ← ComputeServerInternal.ControllerInterface) # NIL THEN {
tempControllerInterface.ExtraCommandAvailable[serverMachineName: ComputeServerInternal.MyNetAddressRope, commandName: procHandle.service, version: procHandle.version ! RPC.CallFailed => {
SummonerControllerControl.ControllerCallFailed[why];
ComputeServerInternal.ControllerInterface ← NIL ;
CONTINUE;
};
];
};
ENDLOOP;
};
[] ← SymTab.Pairs[x: ComputeServerServer.Registry, action: EachPairProc];
};
Startup Stats
InitStats: PUBLIC PROC [myName: RPC.ShortROPE, myAddress: RPC.ShortROPE] = {
serverMachineName ← myName ;
serverMachinePupAddress ← myAddress;
LastRMReclamations ← VMStatistics.rmReclamations;
LastTime ← BasicTime.Now[];
};
Server Stats
TickStatistics: PROC [] = {
lastStatTime: BasicTime.GMT ← BasicTime.Now[];
aSecond: Process.Ticks = Process.SecondsToTicks[1];
WHILE ServiceEnabled DO
nowTime: BasicTime.GMT ← BasicTime.Now[];
period: INT;
period ← BasicTime.Period[lastStatTime, nowTime];
IF period > 0 THEN {
TimeUpThisIncarnation ← TimeUpThisIncarnation + period;
TotalTimeUp ← TotalTimeUp + period;
IF ComputeServerInternal.ActiveServices > 0 THEN {
TimeBusyThisIncarnation ← TimeBusyThisIncarnation + period;
TotalTimeBusy ← TotalTimeBusy + period;
};
};
lastStatTime ← nowTime;
Process.Pause[aSecond];
ENDLOOP;
TimeUpThisIncarnation ← 0;
RequestesThisIncarnation ← 0;
TimeBusyThisIncarnation ← 0;
};
ServerStatistics: Commander.CommandProc = {
IF ServiceEnabled THEN {
percentBusy: REAL;
realTimeBusyThisIncarnation: REAL ← TimeBusyThisIncarnation;
realTimeUpThisIncarnation: REAL ← TimeUpThisIncarnation;
percentBusy ← (100.0 * realTimeBusyThisIncarnation) / MAX[1.0, realTimeUpThisIncarnation];
msg ← IO.PutFR["Seconds up %g, requests %g, time busy %g, percent busy %5.2f%%", IO.int[TimeUpThisIncarnation], IO.int[RequestesThisIncarnation], IO.int[TimeBusyThisIncarnation], IO.real[percentBusy] ]
}
ELSE msg ← "Server not enabled";
};
Server Event Typescript
StartServerEventTypescript: Commander.CommandProc = {
TRUSTED {Process.Detach[FORK ServerEventTypescript[]];};
};
ServerEventTypescript: PROC = {
ENABLE IO.Error => GOTO ViewerDestroyed;
currentViewer: ViewerClasses.Viewer ← NIL;
currentStream: IO.STREAMNIL;
currentEvent: REF READONLY ComputeServerStatistics.ServerEvent ← NIL;
currentViewer ← ViewerOps.CreateViewer[flavor: $Typescript, info: [iconic: FALSE, name: "Compute Server ""Server"" Event Log"]];
currentStream ← ViewerIO.CreateViewerStreams[name: "", viewer: currentViewer].out;
currentStream.PutF["Starting log at %g\n", IO.time[BasicTime.Now[]]];
DO
currentEvent ← ComputeServerStatistics.NextServerEvent[currentEvent];
SELECT currentEvent.type FROM
startService => {
currentStream.PutF["Start %g for %g (%g) at %g\n", IO.rope[currentEvent.command], IO.rope[currentEvent.remoteUser], IO.rope[currentEvent.remoteMachineName], IO.time[currentEvent.startTime]];
};
doneService => {
currentStream.PutF["End %g for %g at %g duration %g seconds\n", IO.rope[currentEvent.command], IO.rope[currentEvent.remoteMachineName], IO.time[currentEvent.endTime], IO.int[BasicTime.Period[currentEvent.startTime, currentEvent.endTime]]];
};
serverEnabled => {
currentStream.PutF[" **** Server enabled at %g\n", IO.time[BasicTime.Now[]]];};
serverDisabled => {
currentStream.PutF[" **** Server disabled at %g\n", IO.time[BasicTime.Now[]]];};
ENDCASE;
ENDLOOP;
EXITS
ViewerDestroyed => {};
};
Initialization
SetTimeouts: PROC [computeVerticalRetrace: BOOL] = TRUSTED {
ticks: Process.Ticks;
cutoff: Process.Ticks;
found: BOOLEAN;
frame: PrincOps.FrameHandle;
IF index = maxIndex THEN {
index ← 0;
FOR i: CARDINAL IN [0..maxIndex) DO
timeout[i].process ← 0;
timeout[i].gfh ← PrincOps.NullGlobalFrame;
timeout[i].pc ← 0;
ENDLOOP;
};
cutoff ← Process.SecondsToTicks[10];
ticks ← PrincOpsUtils.ReadPTC[];
FOR psbi: PrincOps.PsbIndex IN [PrincOps.StartPsb..PrincOps.StartPsb+PrincOps.PDA.count) DO
block: PrincOps.ProcessStateBlock = PrincOps.PDA.block[psbi];
IF PrincOps.PDA.block[psbi].mds = 0 THEN LOOP;
IF PrincOps.PDA.block[psbi].mds - ticks > cutoff THEN LOOP; -- not significant
IF PrincOps.PDA.block[psbi].link.vector
THEN frame ← PrincOps.PDA[PrincOps.PDA.block[psbi].context.state].frame
ELSE frame ← PrincOps.PDA.block[psbi].context.frame;
found ← FALSE;
FOR i: CARDINAL IN [0..index) DO
IF timeout[i].gfh = frame.accesslink AND timeout[i].pc = frame.pc THEN {found ← TRUE; EXIT};
ENDLOOP;
IF found THEN LOOP;
timeout[index] ← [psbi, frame.accesslink, frame.pc];
index ← index + 1;
IF index = maxIndex THEN EXIT;
ENDLOOP;
IF computeVerticalRetrace THEN {
newContext: AMModel.Context;
newProcesses: LIST OF AMProcess.Process;
newContext ← AMModel.MostRecentNamedContext["TerminalImpl", AMModel.RootContext[WorldVM.LocalWorld[]]];
newProcesses ← AMProcess.GetProcesses[LIST[newContext]];
nextVerticalRetraceProcesses ← 0;
DO
stack: AMTypes.TV;
IF newProcesses = NIL THEN EXIT;
[stack: stack] ← AMProcess.GetState[newProcesses.first];
IF stack # NIL THEN {
str: IO.STREAM = IO.ROS[];
PrintTV.Print[stack, str];
IF Rope.Run["TerminalImpl.InternalWaitForBWVerticalRetrace", 0, IO.RopeFromROS[str], 0, FALSE] > 40 THEN {
verticalRetraceProcesses[nextVerticalRetraceProcesses].psbi ← AMProcess.TVToPSBI[newProcesses.first].psbi;
verticalRetraceProcesses[nextVerticalRetraceProcesses].seenCounter ← 0;
nextVerticalRetraceProcesses ← nextVerticalRetraceProcesses + 1;
};
};
AMProcess.Thaw[LIST[newProcesses.first]];
newProcesses ← newProcesses.rest;
ENDLOOP;
};
};
InitializeBackgroundCounting: PROC = {
newContext: AMModel.Context;
Loader.MakeProcedureResident[WhatsActive];
Loader.MakeGlobalFrameResident[InitializeBackgroundCounting];
TRUSTED {Process.InitializeCondition[@whatsActiveProcessDead, 10000]; };
timeout ← NEW[timeOutType];
SafeStorage.PinObject[timeout];
SetTimeouts[TRUE];
TRUSTED {newContext ← AMModel.MostRecentNamedContext["TerminalImpl", AMModel.RootContext[WorldVM.LocalWorld[]]];};
TerminalImplGF ← newContext;
verticalRetraceTV ← Interpreter.Evaluate["TerminalImpl.verticalRetrace"].result;
IF verticalRetraceTV = NIL THEN TRUSTED {
usingVerticalRetrace ← FALSE;
aConditionSomewhere ← @myConditionForCPUStats;
Process.Detach[FORK NotifyCPUStats[]];
}
ELSE TRUSTED {
usingVerticalRetrace ← TRUE;
pointerToVerticalRetraceTV ← AMBridge.PointerFromTV[verticalRetraceTV];
aConditionSomewhere ← pointerToVerticalRetraceTV;
};
};
Commander.Register[key: "///Commands/SummonerServerOn", proc: ServerOn, doc: "SummonerServerOn {controllerName {remoteCommandDirectory {localCommandDirectory}}} Turn on use of the Compute Server Service for cluster controllerName using remoteCommandDirectory on a file server and localCommandDirectory on the workstation"];
Commander.Register[key: "///Commands/SummonerServerOff", proc: ServerOff, doc: "Turn off use of the Compute Server Service"];
Commander.Register[key: "///Commands/SummonerEnableAutoIdle", proc: EnableAutoIdle, doc: "Turn on automatic Summoner server toggle at Idle"];
Commander.Register[key: "///Commands/SummonerDisableAutoIdle", proc: DisableAutoIdle, doc: "Turn off automatic Summoner server toggle at Idle"];
Commander.Register[key: "///Commands/SummonerServerTypescript", proc: StartServerEventTypescript, doc: "Start typescript of server events"];
Commander.Register[key: "///Commands/SummonerServerStatistics", proc: ServerStatistics, doc: "Print statistics"];
[] ← Idle.RegisterIdleHandler[IdleTransition];
InitializeBackgroundCounting[];
END.
Bob Hagmann May 3, 1985 8:24:08 am PDT
changes to: ComputeServerStatsImpl, SendStats
Bob Hagmann January 17, 1986 8:33:08 am PST
Added check for server already enabled in ServerOn
changes to: ServerOn
Bob Hagmann May 5, 1986 11:43:38 am PDT
add idle/background processing and request list code