-- Copyright (C) 1981, 1982, 1984, 1985 by Xerox Corporation. All rights reserved.
-- Policy.mesa, Transport Mechanism Mail Server - policy module
-- HGM, 6-Jul-85 20:03:39
-- Al Hall 8-Jul-82 11:38:07 --
-- Randy Gobbel 20-May-81 12:58:17 --
-- Andrew Birrell 4-Mar-82 14:44:48 --
-- Ted Wobber 29-Aug-84 10:58:45 --
-- Brenda Hankins 24-Aug-84 16:32:05
DIRECTORY
Ascii USING [CR],
EnquiryDefs USING [],
GlassDefs USING [Handle],
LogDefs USING [DisplayNumber, ShowNumber],
PolicyDefs -- using everything -- ,
Process USING [
DisableTimeout, GetPriority, InitializeCondition, MsecToTicks, Priority,
SetPriority, SetTimeout, Ticks],
PupDefs USING [GetPupAddress, PupAddress, PupPackageDestroy, PupPackageMake],
SLDefs USING [GetCount],
Time USING [Current, Pack, Packed, Unpack, Unpacked];
Policy: MONITOR
IMPORTS LogDefs, Process, PupDefs, SLDefs, Time EXPORTS EnquiryDefs, PolicyDefs =
BEGIN
-- Egg-timer --
minsCond: CONDITION;
secsCond: CONDITION;
Wait: PUBLIC PROCEDURE [
days: CARDINAL ← 0, hrs: [0..24) ← 0, mins: [0..60) ← 0, secs: [0..60) ← 0] =
BEGIN
limit: Time.Packed =
LOOPHOLE[Time.Current[] + days * (LONG[24] * 60 * 60) +
hrs * (LONG[60] * 60) + mins * LONG[60] + LONG[secs]];
WaitUntil[limit];
END;
WaitUntil: PUBLIC ENTRY PROC [time: Time.Packed] =
BEGIN
UNTIL Time.Current[] + 60 >= time DO WAIT minsCond ENDLOOP;
UNTIL Time.Current[] >= time DO WAIT secsCond ENDLOOP;
END;
-- Compactor scheduling strategy --
compactorEnabled: BOOLEAN; -- whether compactor should run at all --
compactorWanted: BOOLEAN; -- whether compactor should start another cycle --
compactorDelay: CARDINAL; -- max delay in milliseconds --
compactorStart: CONDITION;
gapsNotified: CARDINAL ← 0; -- number of calls on "GapExists" --
CompactorStart: PUBLIC ENTRY PROCEDURE =
BEGIN
waitFor: CARDINAL ← MAX[25, freeHeap-10];
UNTIL compactorEnabled AND compactorWanted DO WAIT compactorStart ENDLOOP;
UNTIL freeHeap < waitFor DO WAIT compactorStart ENDLOOP;
compactorWanted ← FALSE;
END;
compactorPause: CONDITION;
CompactorPause: PUBLIC ENTRY PROCEDURE =
BEGIN
delay: Process.Ticks = Process.MsecToTicks[
(compactorDelay / (100 - minFreeHeap)) * --beware of overflow!--
(IF freeHeap < minFreeHeap THEN 0 ELSE freeHeap - minFreeHeap)];
UNTIL compactorEnabled DO WAIT compactorPause ENDLOOP;
IF current[work] = 0 THEN RETURN;
IF gapsNotified > 0 THEN {gapsNotified ← gapsNotified - 1; RETURN};
IF delay = 0 THEN RETURN;
Process.SetTimeout[@compactorPause, delay];
WAIT compactorPause;
END;
freeHeap: [0..100];
minFreeHeap: [0..100]; -- min free heap for running compactor with pauses --
loggedHeap: [0..100] ← 100; -- Free heap recorded in log
AmountOfFreeHeap: PUBLIC ENTRY PROCEDURE [given: [0..100]] =
BEGIN
freeHeap ← given;
IF given # loggedHeap
AND
(given < minFreeHeap OR loggedHeap < minFreeHeap
OR given NOT IN (loggedHeap - 5..loggedHeap + 5)) THEN LogFreeHeap[];
END;
LogFreeHeap: INTERNAL PROCEDURE =
BEGIN
LogDefs.ShowNumber["Free heap: "L, freeHeap, "%"L];
loggedHeap ← freeHeap;
END;
GapExists: PUBLIC ENTRY PROCEDURE =
BEGIN
compactorWanted ← TRUE;
NOTIFY compactorStart;
IF gapsNotified = 0 THEN NOTIFY compactorPause;
gapsNotified ← gapsNotified + 1;
END;
-- Other time delays --
periodicWantedNow: PACKED ARRAY PolicyDefs.PeriodicProcess OF BOOLEAN ← ALL[
FALSE];
readPendingDelay: CARDINAL ← 15; -- minutes --
prodServersDelay: CARDINAL ← 15; -- minutes --
archiverHour: [0..24) ← 23; -- time of day, before IFS Archiver
regPurgerHour: [0..24) ← 0; -- [0..56) - see Init
PeriodicWait: PUBLIC ENTRY PROC [process: PolicyDefs.PeriodicProcess] =
BEGIN
limit: LONG CARDINAL =
SELECT process FROM
readPending => LOOPHOLE[Time.Current[] + readPendingDelay * 60],
prodServers => LOOPHOLE[Time.Current[] + prodServersDelay * 60],
archiver => CalculateNextTime[archiverHour],
regPurger => CalculateNextTime[regPurgerHour],
ENDCASE => ERROR;
UNTIL Time.Current[] >= limit OR periodicWantedNow[process] DO
WAIT minsCond ENDLOOP;
periodicWantedNow[process] ← FALSE;
END;
Activate: PUBLIC ENTRY PROC [process: PolicyDefs.PeriodicProcess] =
BEGIN periodicWantedNow[process] ← TRUE; BROADCAST minsCond; END;
CalculateNextTime: PROC [wantedHour: [0..24)] RETURNS [Time.Packed] =
BEGIN
unpacked: Time.Unpacked ← Time.Unpack[Time.Current[]];
IF unpacked.hour >= wantedHour THEN -- move to next day --
unpacked ← Time.Unpack[LOOPHOLE[Time.Current[] + 24 * 60 * LONG[60]]];
unpacked.minute ← 0;
unpacked.second ← 0;
unpacked.hour ← wantedHour;
RETURN[Time.Pack[unpacked, FALSE]]
END;
PendingAllowed: PROC RETURNS [BOOLEAN] =
BEGIN
IF SLDefs.GetCount[forward] > 10 THEN RETURN[FALSE];
RETURN[TRUE];
END;
ExpressAllowed: PROC RETURNS [BOOLEAN] =
BEGIN
IF SLDefs.GetCount[input] > 0 THEN RETURN[FALSE];
IF SLDefs.GetCount[forward] > 5 THEN RETURN[FALSE];
RETURN[TRUE];
END;
-- Control on operations --
control: PACKED ARRAY PolicyDefs.Operation OF PolicyDefs.Control;
current: ARRAY PolicyDefs.Operation OF PolicyDefs.OpLimit;
high: ARRAY PolicyDefs.Operation OF PolicyDefs.OpLimit;
reject: ARRAY PolicyDefs.Operation OF LONG CARDINAL;
total: ARRAY PolicyDefs.Operation OF LONG CARDINAL;
opWait: CONDITION;
WaitOperation: PUBLIC ENTRY PROCEDURE [op: PolicyDefs.Operation] = {
UNTIL CheckOp[op, TRUE] DO WAIT opWait ENDLOOP};
CheckOperation: PUBLIC ENTRY PROC [
op: PolicyDefs.Operation, set: BOOLEAN ← TRUE] RETURNS [BOOLEAN] = {
RETURN[CheckOp[op, set]]};
CheckOp: INTERNAL PROCEDURE [op: PolicyDefs.Operation, set: BOOLEAN]
RETURNS [BOOLEAN] =
BEGIN
IF current[op] < control[op].limit AND control[op].allowed
AND
(SELECT op FROM
clientInput, serverInput =>
(freeHeap > minFreeHeap / 2 AND CheckOp[connection, set]),
readMail, regExpand, FTP => CheckOp[connection, set],
readExpress => ExpressAllowed [] AND CheckOp[mainLine, set],
readPending => PendingAllowed[] AND CheckOp[mainLine, set],
readInput, readForward, readMailbox => CheckOp[mainLine, set],
remailing => (freeHeap > minFreeHeap / 2 AND CheckOp[mainLine, set]),
RSReadMail, MSReadMail, archiver, regPurger => CheckOp[
background, set],
connection, telnet, mainLine, background => CheckOp[work, set],
work => TRUE,
ENDCASE => ERROR) THEN
BEGIN
IF set THEN {
current[op] ← current[op] + 1;
IF current[op] > high[op] THEN high[op] ← current[op];
total[op] ← total[op] + 1};
RETURN[TRUE]
END
ELSE {IF set THEN reject[op] ← reject[op] + 1; RETURN[FALSE]};
END;
EndOperation: PUBLIC ENTRY PROCEDURE [op: PolicyDefs.Operation] = {EndOp[op]};
EndOp: INTERNAL PROCEDURE [op: PolicyDefs.Operation] =
BEGIN
current[op] ← current[op] - 1;
SELECT op FROM
clientInput, serverInput, readMail, regExpand, FTP =>
EndOp[connection];
readExpress, readInput, readPending, readForward, readMailbox, remailing =>
EndOp[mainLine];
RSReadMail, MSReadMail, archiver, regPurger => EndOp[background];
connection, telnet, mainLine, background => EndOp[work];
work => NULL;
ENDCASE => ERROR;
BROADCAST opWait;
END;
ReadOperationCurrent: PUBLIC ENTRY PROC [op: PolicyDefs.Operation]
RETURNS [PolicyDefs.OpLimit] = {RETURN[current[op]]};
ReadOperationControl: PUBLIC ENTRY PROCEDURE [op: PolicyDefs.Operation]
RETURNS [PolicyDefs.Control] = BEGIN RETURN[control[op]] END;
SetOperationLimit: PUBLIC ENTRY PROCEDURE [
op: PolicyDefs.Operation, limit: PolicyDefs.OpLimit] =
BEGIN control[op].limit ← limit; BROADCAST opWait; END;
SetOperationAllowed: PUBLIC ENTRY PROCEDURE [
op: PolicyDefs.Operation, allowed: BOOLEAN] =
BEGIN control[op].allowed ← allowed; BROADCAST opWait; END;
SetTelnetAllowed: PUBLIC ENTRY PROCEDURE =
BEGIN control[work].allowed ← control[telnet].allowed ← TRUE; END;
PolicyControls: PUBLIC PROC [str: GlassDefs.Handle] =
BEGIN OPEN str;
WriteChar[Ascii.CR];
WriteString["Operation: Allowed Limit Current High Reject Accepted"L];
-- clientInput yes 127 127 127 65535 655355555 --
FOR op: PolicyDefs.Operation IN PolicyDefs.Operation DO
control: PolicyDefs.Control = ReadOperationControl[op];
gap: STRING = " "L;
WriteChar[Ascii.CR];
WriteString[
SELECT op FROM
work => "work "L,
connection => " connection "L,
clientInput => " clientInput "L,
serverInput => " serverInput "L,
readMail => " readMail "L,
regExpand => " regExpand "L,
FTP => " FTP "L,
telnet => " Telnet "L,
mainLine => " mainLine "L,
readExpress => " readExpress "L,
readInput => " readInput "L,
readPending => " readPending "L,
readForward => " readForward "L,
readMailbox => " readMailbox "L,
remailing => " remailing "L,
background => " background "L,
RSReadMail => " RSReadMail "L,
MSReadMail => " MSReadMail "L,
archiver => " archiver "L,
regPurger => " RegPurger "L,
ENDCASE => ERROR];
WriteString[gap];
WriteString[IF control.allowed THEN "yes"L ELSE "no"L];
WriteString[gap];
WriteDecimal[control.limit];
WriteString[gap];
WriteDecimal[current[op]];
WriteString[gap];
WriteDecimal[high[op]];
WriteString[gap];
WriteLongDecimal[reject[op]];
WriteString[gap];
WriteLongDecimal[total[op]];
WriteString[gap];
ENDLOOP;
WriteChar[Ascii.CR];
WriteString["readPendingDelay="L];
WriteDecimal[readPendingDelay];
WriteString[" mins"L];
WriteChar[Ascii.CR];
WriteString["prodServersDelay="L];
WriteDecimal[prodServersDelay];
WriteString[" mins"L];
END;
-- misc procedures for use from the debugger: use with care! --
BroadcastCondition: ENTRY PROC [cond: POINTER TO CONDITION] = {BROADCAST cond↑};
forever: CONDITION; -- time-out is disabled --
WaitOnCondition: ENTRY PROC [cond: POINTER TO CONDITION] = {WAIT cond↑};
Ready: SIGNAL = CODE;
SignalAtPriority: PROC [new: Process.Priority] =
BEGIN
old: Process.Priority = Process.GetPriority[];
Process.SetPriority[new];
SIGNAL Ready[];
Process.SetPriority[old];
END;
-- Initialisation --
Init: ENTRY PROCEDURE =
BEGIN OPEN Process;
-- Egg-timer --
InitializeCondition[@minsCond, MsecToTicks[60000]];
InitializeCondition[@secsCond, MsecToTicks[1000]];
-- Compactor scheduling --
compactorEnabled ← TRUE;
compactorWanted ← TRUE;
InitializeCondition[@compactorStart, 0];
DisableTimeout[@compactorStart];
compactorDelay ← 1000;
InitializeCondition[@compactorPause, MsecToTicks[compactorDelay]];
minFreeHeap ← 10;
freeHeap ← (minFreeHeap + 100) / 2;
-- Operation controls --
BEGIN
max: PolicyDefs.OpLimit = LAST[PolicyDefs.OpLimit];
control[work] ← [limit: max, allowed: TRUE];
control[connection] ← [limit: 12, allowed: TRUE];
control[clientInput] ← [limit: 5, allowed: TRUE];
control[serverInput] ← [limit: 5, allowed: TRUE];
control[readMail] ← [limit: 8, allowed: TRUE];
control[regExpand] ← [limit: 9, allowed: TRUE];
control[FTP] ← [limit: 2, allowed: TRUE];
control[telnet] ← [limit: 3, allowed: TRUE];
control[mainLine] ← [limit: max, allowed: TRUE];
control[readExpress] ← [limit: 1, allowed: TRUE];
control[readInput] ← [limit: 1, allowed: TRUE];
control[readPending] ← [limit: 1, allowed: TRUE];
control[readForward] ← [limit: 2, allowed: TRUE];
control[readMailbox] ← [limit: 1, allowed: TRUE];
control[background] ← [limit: 1, allowed: TRUE];
control[RSReadMail] ← [limit: 1, allowed: TRUE];
control[MSReadMail] ← [limit: 1, allowed: TRUE];
control[remailing] ← [limit: 1, allowed: TRUE];
control[archiver] ← [limit: 1, allowed: TRUE];
control[regPurger] ← [limit: 1, allowed: TRUE];
END;
current ← high ← ALL[0];
reject ← total ← ALL[LONG[0]];
InitializeCondition[@opWait, 0];
DisableTimeout[@opWait];
DisableTimeout[@forever];
BEGIN
-- RegPurger takes a LONG LONG LONG time, and it clogs up the R Server.
-- This is a hack to prevent all of them in one area running at the same time.
me: PupDefs.PupAddress;
[] ← PupDefs.PupPackageMake[];
PupDefs.GetPupAddress[@me, "ME"L];
PupDefs.PupPackageDestroy[];
regPurgerHour ← me.host MOD 6;
END;
-- statistics --
LogDefs.DisplayNumber["Free heap"L, [percent[@freeHeap]]];
LogDefs.DisplayNumber["Connections"L, [short[@(current[connection])]]];
END;
Init[];
END.