DIRECTORY Basics, BasicTime, CedarProcess, Commander, CommandTool, Containers, FS, Icons, IO, IOClasses, List, MakeDo, MakeDoBasics, MakeDoPrivate, MessageWindow, MoreIOClasses, Process, ProcessProps, RedBlackTree, Rope, TypeScript, UserProfile, ViewerClasses, ViewerEvents, ViewerIO, ViewerOps; MakeDoBasicImpl: CEDAR MONITOR IMPORTS CedarProcess, CommandTool, Containers, Icons, IO, IOClasses, List, MakeDo, MakeDoPrivate, MessageWindow, MoreIOClasses, Process, RedBlackTree, Rope, TypeScript, UserProfile, ViewerEvents, ViewerIO, ViewerOps EXPORTS MakeDo, MakeDoBasics, MakeDoPrivate = BEGIN OPEN MakeDo, MakeDoPrivate; NodeRep: PUBLIC TYPE = MakeDoPrivate.NodeRep; ActionRep: PUBLIC TYPE = MakeDoPrivate.ActionRep; NodeClassRep: PUBLIC TYPE = MakeDoPrivate.NodeClassRep; Viewer: TYPE = ViewerClasses.Viewer; CHList: TYPE = LIST OF Commander.Handle; CHTList: TYPE = RECORD [head, tail: CHList _ NIL]; in: BOOL _ FALSE; whosIn: UNSAFE PROCESS; whatIn: PROC; monitorChange: CONDITION; entryTimeout: INT _ 60; DoIn: PUBLIC PROC [p: PROC] = { CedarProcess.CheckAbort[]; Enter[p]; p[!UNWIND => Exit[]]; Exit[]; }; Enter: ENTRY PROC [p: PROC] = { ENABLE UNWIND => NULL; WHILE in DO WAIT monitorChange ENDLOOP; in _ TRUE; whosIn _ Process.GetCurrent[]; TRUSTED {whatIn _ p}; }; Exit: ENTRY PROC = { ENABLE UNWIND => NULL; in _ FALSE; whosIn _ NIL; whatIn _ NIL; NOTIFY monitorChange; }; processUse: INTEGER _ 0; processAllocation: INTEGER _ 0; SetProcessAllocation: PUBLIC --ENTRY-- PROC [n: NAT] = { SPAWork: --INTERNAL-- PROC = { processAllocation _ n; BroadcastQueue[]; }; DoIn[SPAWork]; }; SetProcessAllocationFromProfile: PROC [reason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- = { SELECT reason FROM rollBack, firstTime => { pa: INT _ UserProfile.Number["MakeDo.AuxilliaryProcessAllocation", 5]; SetProcessAllocation[pa]; }; edit => { reason _ reason; }; ENDCASE => ERROR; }; EndFork: --ENTRY-- PROC = { InnerEndFork: --INTERNAL-- PROC = { processUse _ processUse - 1; BroadcastQueue[]; }; DoIn[InnerEndFork]; }; halt: BOOL _ FALSE; readyQueue: NodeList _ NIL; workCount: INTEGER _ 0; waitQueue: RefTable _ MakeRefTable[]; queueChange: CONDITION; queueTimeout: INT _ 60; BroadcastQueue: ENTRY PROC = { ENABLE UNWIND => NULL; BROADCAST queueChange; }; NotifyQueue: ENTRY PROC = { ENABLE UNWIND => NULL; NOTIFY queueChange; }; WaitQueue: ENTRY PROC = { ENABLE UNWIND => NULL; WAIT queueChange; }; SetState: --INTERNAL-- PROC [n: Node, new: NodeState] = { ENABLE UNWIND => NULL; SELECT n.state FROM inactive => NULL; ready => readyQueue _ readyQueue.rest; working => workCount _ workCount - 1; waiting => IF DeleteFromRefTable[n, waitQueue] # n THEN ERROR; ENDCASE => ERROR; n.state _ new; SELECT n.state FROM inactive => NULL; ready => { readyQueue _ CONS[n, readyQueue]; }; working => workCount _ workCount + 1; waiting => { waitQueue.Insert[n, n]; }; ENDCASE => ERROR; NotifyQueue[]; }; InnerEnqueue: --INTERNAL-- PROC [job: Job, n: Node, inServiceOf: Node _ NIL] = { IF n.state = inactive THEN { n.someoneWhoCares _ job; SetState[n, ready]; }; IF inServiceOf # NIL THEN { n.waitingOnCurrent _ CONS[inServiceOf, n.waitingOnCurrent]; inServiceOf.waitCount _ inServiceOf.waitCount + 1; SELECT inServiceOf.state FROM inactive, ready => ERROR; working => SetState[inServiceOf, waiting]; waiting => n _ n; ENDCASE => ERROR; }; }; InnerEnsureMembership: --INTERNAL-- PROC [job: Job, ns: NodeSet, n: Node] = { IF ns.nodes.Lookup[n] = NIL THEN { ns.nodes.Insert[n, n]; n.memberships.Insert[ns, ns]; CountNode[ns, n, 1]; IF n.current#true AND n.state = inactive THEN InnerEnqueue[job, n, NIL]; }; }; DestroyNodeSet: --ENTRY-- PROC [ns: NodeSet] = { DNSWork: --INTERNAL-- PROC = { UnMemberize: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { n: Node = NARROW[data]; IF n.memberships.Delete[ns] = NIL THEN ERROR; }; StatelessEnumerateRefTable[ns.nodes, UnMemberize]; ns^ _ [nodes: ns.nodes]; ns.nodes.DestroyTable[]; }; DoIn[DNSWork]; }; CountNode: --INTERNAL-- PROC [ns: NodeSet, n: Node, D: INT] = { SELECT n.current FROM notComputed, false => ns.nonCurrents _ ns.nonCurrents + D; true => { ns.currents _ ns.currents + D; SELECT n.broken FROM FALSE => ns.ok _ ns.ok + D; TRUE => ns.broken _ ns.broken + D; ENDCASE => ERROR; }; ENDCASE => ERROR; }; SetCurrency: PUBLIC --INTERNAL-- PROC [n: Node, current: ExpensiveBool] = { Discount: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { ns: NodeSet = NARROW[data]; CountNode[ns, n, -1]; }; Count: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { ns: NodeSet = NARROW[data]; CountNode[ns, n, 1]; }; IF n.current = current THEN RETURN; StatelessEnumerateRefTable[n.memberships, Discount]; n.current _ current; StatelessEnumerateRefTable[n.memberships, Count]; }; SetBroken: PUBLIC --INTERNAL-- PROC [n: Node, broken: BOOL] = { Discount: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { ns: NodeSet = NARROW[data]; CountNode[ns, n, -1]; }; Count: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { ns: NodeSet = NARROW[data]; CountNode[ns, n, 1]; }; IF n.broken = broken THEN RETURN; StatelessEnumerateRefTable[n.memberships, Discount]; n.broken _ broken; StatelessEnumerateRefTable[n.memberships, Count]; }; NoteAchievement: --INTERNAL-- PROC [goal: Node] = { SetCurrency[goal, true]; IF goal.state # working THEN ERROR; InnerGiveUp[goal]; }; GiveUp: --ENTRY-- PROC [goal: Node] = { GUWork: --INTERNAL-- PROC = {InnerGiveUp[goal]}; DoIn[GUWork]; }; InnerGiveUp: --INTERNAL-- PROC [goal: Node] = { IF goal.state = working THEN { SetState[goal, inactive]; goal.someoneWhoCares _ NIL; InnerWakeWaiters[goal]; }; IF readyQueue=NIL AND workCount=0 AND waitQueue.Size[]=0 THEN halt _ FALSE; }; InnerWakeWaiters: --INTERNAL-- PROC [n: Node] = { WHILE n.waitingOnCurrent # NIL DO waiter: Node = n.waitingOnCurrent.first; n.waitingOnCurrent _ n.waitingOnCurrent.rest; IF waiter.state # waiting THEN ERROR; waiter.waitCount _ waiter.waitCount - 1; SELECT waiter.waitCount FROM <0 => ERROR; =0 => SetState[waiter, ready]; >0 => NULL; ENDCASE => ERROR; ENDLOOP; }; Ensure: PUBLIC PROC [goals: RefTable, modifiabilitySpec: ModifiabilitySpec, parent: Commander.Handle] RETURNS [okGoalCount, nonOKGoalCount, nSteps: NAT, failedSteps: ActionList, nonOKGoalList: NodeList] = { callingJob: Job _ NEW [JobRep _ [ wDir: CommandTool.CurrentWorkingDirectory[], goals: goals, processes: MakeRefTable[], otherModifiable: modifiabilitySpec ]]; goalNS: NodeSet = NEW [NodeSetPrivate _ [ nodes: MakeRefTable[] ]]; NoteGoal: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { n: Node = NARROW[data]; Work: --INTERNAL-- PROC = { InnerSetModifiability[n, callingJob.goals, callingJob.otherModifiable]; InnerEnsureMembership[callingJob, goalNS, n]; }; DoIn[Work]; }; {ENABLE UNWIND => { NoteEnsureUnwind[]; }; StatelessEnumerateRefTable[goals, NoteGoal]; [okGoalCount, nonOKGoalCount, nonOKGoalList] _ HelpEmptyQueue[goalNS: goalNS, callingJob: callingJob, parent: parent]; EnsureWDir[callingJob.wDir, parent]; }; nSteps _ callingJob.nSteps; failedSteps _ callingJob.failedSteps; goals.DestroyTable[]; DestroyNodeSet[goalNS]; }; NoteEnsureUnwind: --ENTRY-- PROC = { NEUWork: --INTERNAL-- PROC = { x: BOOL _ FALSE; IF readyQueue#NIL OR workCount#0 OR waitQueue.Size[]#0 THEN { halt _ TRUE; WHILE readyQueue # NIL DO victim: Node = readyQueue.first; SetState[victim, working]; InnerGiveUp[victim]; ENDLOOP; x _ x; }; }; DoIn[NEUWork]; }; HelpEmptyQueue: PROC [goalNS: NodeSet, callingJob: Job, parent: Commander.Handle] RETURNS [okGoalCount, nonOKGoalCount: NAT, nonOKGoalList: NodeList] = { nonOKGoalList _ nonOKGoalList; DO ENABLE ABORTED => { KillIt: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { process: CedarProcess.Process = NARROW[data]; TRUSTED {Process.Abort[process.process]}; }; StatelessEnumerateRefTable[callingJob.processes, KillIt]; }; caringJob: Job _ NIL; goal: Node _ NIL; toExecute: Action _ NIL; consistencyReason: ROPE _ NIL; fails: ExpensiveBool; haveWorkers, fork: BOOL; [caringJob, goal, toExecute, consistencyReason, fails, haveWorkers, okGoalCount, nonOKGoalCount, nonOKGoalList, fork] _ GetActionToExecute[goalNS, callingJob, parent]; IF toExecute = NIL THEN EXIT; {ENABLE UNWIND => { ReturnPermission[toExecute]; GiveUp[goal]; IF fork THEN EndFork[]; }; e: Execution = NEW [ExecutionPrivate _ [caringJob, toExecute, goal, parent, parent, fork]]; toExecute.reasonWhyLastDone _ consistencyReason; IF fails = false THEN Warning[IO.PutFR["Retrying %g", [rope[toExecute.cmd]]]]; IF debugging THEN Confirm[e.a.cmd]; IF fork THEN { e.bch _ Buffer[e.ch]; IF fork THEN Msg[parent, "%lForking %g%l\n", [rope["e"]], [rope[e.a.cmd]], [rope["E"]]]; e.process _ CedarProcess.Fork[Execute, e, [inheritProperties: TRUE]]; caringJob.processes.Insert[e.process, e.process]; } ELSE { [] _ Execute[e]; }; }; ENDLOOP; nonOKGoalList _ nonOKGoalList; }; Execution: TYPE = REF ExecutionPrivate; ExecutionPrivate: TYPE = RECORD [job: Job, a: Action, goal: Node, bch, ch: Commander.Handle, forked: BOOL, process: CedarProcess.Process _ NIL]; Execute: PROC [data: REF ANY] RETURNS [results: REF ANY _ NIL] --CedarProcess.ForkableProc-- = { e: Execution = NARROW[data]; {OPEN e; ENABLE UNWIND => { ReturnPermission[a]; GiveUp[goal]; IF e.forked THEN EndFork[]; }; failed: BOOL; IF forked THEN WHILE e.process=NIL OR job.processes.Lookup[e.process]#e.process DO Process.Pause[Process.SecondsToTicks[1]] ENDLOOP; IncrementStepCount[job]; failed _ CommandTool.DoCommand[a.cmd, bch] = $Failure; IF forked THEN Flush[bch, ch]; a.fails _ Expensify[failed]; IF failed THEN AddFailedCmd[job, a]; EnumerateResults[a, InnerSuspectNodeChange]; CheckIn[job, goal, a, e.process]; }; IF e.forked THEN EndFork[]; }; bufKey: ATOM = $MakeDoBuffer; tsKey: ATOM = $MakeDoBufferTypescript; auxBoxIcon: Icons.IconFlavor _ Icons.NewIconFromFile["MakeDo.Icons", 0]; auxHeight: INTEGER _ 100; auxBox: Viewer _ NIL; auxBoxBottom: INTEGER _ 0; auxBoxList: CHTList _ []; Buffer: ENTRY PROC [ch: Commander.Handle] RETURNS [bch: Commander.Handle] = { ENABLE UNWIND => NULL; bufout: IO.STREAM = MoreIOClasses.CreateBuffer[]; ts: Viewer; this: CHList; IF auxBox = NIL OR auxBox.destroyed THEN { auxBox _ Containers.Create[info: [iconic: TRUE, name: "MakeDoAuxBox", icon: auxBoxIcon, column: right]]; [] _ ViewerEvents.RegisterEventProc[DestroyGuard, destroy, auxBox]; auxBoxBottom _ 0; auxBoxList _ []; }; ts _ TypeScript.Create[ info: [parent: auxBox, wy: auxBoxBottom, wh: auxHeight, wx: 0, ww: auxBox.cw], paint: TRUE]; Containers.ChildXBound[auxBox, ts]; auxBoxBottom _ ts.wy + ts.wh; bch _ NEW [Commander.CommandObject _ [ commandLine: "Shouldn't care", propertyList: List.PutAssoc[tsKey, ts, List.PutAssoc[bufKey, bufout, CommandTool.CopyAList[ch.propertyList]]] ]]; [in: bch.in, out: bch.out] _ ViewerIO.CreateViewerStreams[name: "Jose Frink", viewer: ts]; bch.err _ bch.out _ IOClasses.CreateDribbleOutputStream[bufout, bch.out]; this _ LIST[bch]; IF auxBoxList.tail # NIL THEN auxBoxList.tail.rest _ this ELSE auxBoxList.head _ this; auxBoxList.tail _ this; }; DestroyGuard: ENTRY PROC [viewer: Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE] --ViewerEvents.EventProc-- = { ENABLE UNWIND => NULL; IF auxBoxList.head # NIL THEN { MessageWindow.Append["You don't really want to delete this viewer --- some processes are may still write into subviewers", TRUE]; MessageWindow.Blink[]; abort _ TRUE; }; }; Msg: ENTRY PROC [ch: Commander.Handle, format: ROPE, v1, v2, v3, v4, v5: IO.Value _ [null[]]] = { ENABLE UNWIND => NULL; ch.out.PutF[format, v1, v2, v3, v4, v5]; }; Flush: ENTRY PROC [bch, ch: Commander.Handle] = { ENABLE UNWIND => NULL; buffer: IO.STREAM = NARROW[List.Assoc[bufKey, bch.propertyList]]; ts: Viewer = NARROW[List.Assoc[tsKey, bch.propertyList]]; last: CHList _ NIL; MoreIOClasses.SendBuffer[buffer, ch.out, TRUE]; IF auxBox = NIL OR auxBox.destroyed THEN { auxBoxBottom _ 0; auxBoxList _ []; }; FOR cur: CHList _ auxBoxList.head, cur.rest WHILE cur # NIL DO IF cur.first = bch THEN { auxBoxBottom _ ts.wy; IF last # NIL THEN last.rest _ cur.rest ELSE auxBoxList.head _ cur.rest; IF cur = auxBoxList.tail THEN { IF cur.rest # NIL THEN ERROR; auxBoxList.tail _ last; }; ViewerOps.DestroyViewer[ts, FALSE]; FOR rest: CHList _ cur.rest, rest.rest WHILE rest # NIL DO obch: Commander.Handle = rest.first; ts: Viewer = NARROW[List.Assoc[tsKey, obch.propertyList]]; ViewerOps.MoveViewer[ts, 0, auxBoxBottom, ts.ww, ts.wh, FALSE]; auxBoxBottom _ ts.wy + ts.wh; ENDLOOP; ViewerOps.PaintViewer[auxBox, client]; EXIT; }; last _ cur; ENDLOOP; }; SetModifiability: --ENTRY-- PROC [n: Node, goals: RefTable, ms: ModifiabilitySpec] = { SMWork: --INTERNAL-- PROC = {InnerSetModifiability[n, goals, ms]}; DoIn[SMWork]; }; InnerSetModifiability: PUBLIC --INTERNAL-- PROC [n: Node, goals: RefTable, ms: ModifiabilitySpec] = { new: Modifiability _ SELECT TRUE FROM goals.Lookup[n] = n => yes, ms = NIL => guess, ENDCASE => SELECT ms.Lookup[n] FROM n => yes, NIL => no, ENDCASE => ERROR; IF n.modifiability = new THEN RETURN; n.modifiability _ new; InnerUncurrentNode[n]; n _ n; }; GetActionToExecute: --ENTRY-- PROC [goalNS: NodeSet, callingJob: Job, parent: Commander.Handle] RETURNS [job: Job, goal: Node, toExecute: Action _ NIL, consistencyReason: ROPE, fails: ExpensiveBool, haveWorkers: BOOL _ FALSE, okGoalCount, nonOKGoalCount: NAT, nonOKGoalList: NodeList, fork: BOOL] = { GAEWork: --INTERNAL-- PROC = { job _ job; DO a: Action; sourceMissing: BOOL _ FALSE; sourceBroken: BOOL _ FALSE; waits: BOOL _ FALSE; missedList: NodeList _ NIL; CheckSource: --INTERNAL-- PROC [n: Node, which: ActionDep, optional: BOOL] = { CedarProcess.CheckAbort[]; InnerSetModifiability[n, job.goals, job.otherModifiable]; IF (NOT optional) AND n.created = notExistTime THEN { sourceMissing _ TRUE; IF goal.modifiability=yes THEN missedList _ CONS[n, missedList]; }; SELECT n.current FROM true => { IF n.broken THEN SetBroken[goal, sourceBroken _ TRUE]; RETURN; }; false => SetCurrency[goal, false]; notComputed => NULL; ENDCASE => ERROR; InnerEnqueue[job, n, goal]; waits _ TRUE; }; IF goal # NIL AND goal.state = working THEN ERROR; DO CedarProcess.CheckAbort[]; IF goalNS.nonCurrents=0 THEN { SurveyNode: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { n: Node = NARROW[data]; IF n.current=true AND NOT n.broken THEN { okGoalCount _ okGoalCount + 1; } ELSE { nonOKGoalCount _ nonOKGoalCount + 1; nonOKGoalList _ CONS[n, nonOKGoalList]; }; }; okGoalCount _ nonOKGoalCount _ 0; nonOKGoalList _ NIL; StatelessEnumerateRefTable[goalNS.nodes, SurveyNode]; RETURN}; IF readyQueue # NIL THEN EXIT; IF workCount = 0 THEN { IF waitQueue.Size[] # 0 THEN { FindCycles[]; LOOP}; { EnsureQueued: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { n: Node = NARROW[data]; IF n.state # inactive THEN ERROR; IF n.current#true THEN InnerEnqueue[callingJob, n, NIL]; }; StatelessEnumerateRefTable[goalNS.nodes, EnsureQueued]; IF readyQueue = NIL THEN ERROR; LOOP; }; }; Exit[]; WaitQueue[ ! UNWIND => Enter[GAEWork] ]; Enter[GAEWork]; ENDLOOP; goal _ readyQueue.first; IF goal.state # ready THEN ERROR; SetState[goal, working]; IF halt THEN {InnerGiveUp[goal]; LOOP}; {ENABLE UNWIND => InnerGiveUp[goal]; IF (job _ goal.someoneWhoCares) = NIL THEN ERROR; EnsureWDir[job.wDir, parent]; IF goal.current=true THEN ERROR --shouldn't have current nodes in queue--; IF debugging THEN Log["Examining %g", [rope[goal.name]] ]; SetBroken[goal, goal.modifiability=yes AND goal.created=notExistTime]; IF goal.producer = NIL THEN InnerGetProduced[goal]; IF InnerLeaf[goal] THEN {NoteAchievement[goal]; LOOP}; a _ goal.producer.a; IF a.permissionGranted THEN { a.waitingForPermission _ CONS[goal, a.waitingForPermission]; goal.waitCount _ 1; SetState[goal, waiting]; LOOP}; a.permissionGranted _ TRUE; a.queuedFailsInvalidation _ FALSE; {ENABLE UNWIND => InnerReturnPermission[a]; IF debugging THEN Log["Examining %g", [rope[a.cmd]] ]; InnerEnumerateSources[a, cmd, CheckSource]; IF waits THEN {InnerReturnPermission[a]; LOOP}; IF sourceBroken THEN {InnerReturnPermission[a]; NoteAchievement[goal]; LOOP}; SELECT a.derivedFromCurrentDeterminers FROM TRUE => NULL; FALSE => { InnerRederive[a]; a.derivedFromCurrentDeterminers _ TRUE; }; ENDCASE => ERROR; sourceMissing _ FALSE; InnerEnumerateSources[a, data, CheckSource]; IF waits THEN {InnerReturnPermission[a]; LOOP}; IF sourceBroken THEN {InnerReturnPermission[a]; NoteAchievement[goal]; LOOP}; IF sourceMissing THEN { IF goal.modifiability=yes THEN { SetBroken[goal, TRUE]; Warning[IO.PutFR[ "Couldn't %g because %g don't exist", [rope[a.cmd]], [rope[EnglishList[missedList].el]] ]]; }; InnerReturnPermission[a]; NoteAchievement[goal]; LOOP}; SELECT fails _ a.fails FROM true => {SetBroken[goal, TRUE]; InnerReturnPermission[a]; NoteAchievement[goal]; LOOP}; false, notComputed => NULL; ENDCASE => ERROR; IF NOT goal.consistencyAsked THEN { [goal.consistent, goal.consistencyReason] _ a.class.CheckConsistency[a, goal]; goal.consistencyAsked _ TRUE; }; consistencyReason _ goal.consistencyReason; IF goal.consistent THEN {InnerReturnPermission[a]; NoteAchievement[goal]; LOOP}; toExecute _ a; SetCurrency[goal, false]; DO SELECT processAllocation FROM =0 => {fork _ FALSE; EXIT}; >processUse => {fork _ TRUE; processUse _ processUse+1; EXIT}; ENDCASE => { Exit[]; WaitQueue[ ! UNWIND => Enter[GAEWork] ]; Enter[GAEWork]; }; ENDLOOP; RETURN; }; }; ENDLOOP; }; goal _ NIL; DoIn[GAEWork]; }; CheckIn: --ENTRY-- PROC [job: Job, goal: Node, producer: Action, process: CedarProcess.Process] = { CIWork: --INTERNAL-- PROC = { IF goal.state # working THEN ERROR; SetState[goal, ready]; InnerReturnPermission[producer]; IF process # NIL THEN [] _ job.processes.Delete[process]; }; DoIn[CIWork]; }; ReturnPermission: --ENTRY-- PROC [a: Action] = { RPWork: --INTERNAL-- PROC = {InnerReturnPermission[a]}; DoIn[RPWork]; }; InnerReturnPermission: --INTERNAL-- PROC [a: Action] = { a.permissionGranted _ FALSE; IF a.queuedFailsInvalidation THEN a.fails _ notComputed; a.queuedFailsInvalidation _ FALSE; InnerWakePermissionWaiters[a]; }; InnerWakePermissionWaiters: --INTERNAL-- PROC [a: Action] = { a _ a; WHILE a.waitingForPermission # NIL DO waiter: Node = a.waitingForPermission.first; a.waitingForPermission _ a.waitingForPermission.rest; IF waiter.state # waiting THEN ERROR; IF waiter.waitCount # 1 THEN ERROR; waiter.waitCount _ 0; SetState[waiter, ready]; ENDLOOP; a _ a; }; InnerGetProduced: PUBLIC --INTERNAL-- PROC [n: Node] = { n2: Node; IF n.producer # NIL THEN ERROR; SELECT n.current FROM true, false => ERROR --we shouldn't know that much if we haven't tried to produce yet--; notComputed => NULL; ENDCASE => ERROR; n2 _ TryToProduce[n.name, n.class]; IF n # n2 THEN Warning[IO.PutFR["Disagreement on cannonical name for %g or %g", IO.rope[n.name], IO.rope[n2.name]]]; InnerVolunteerLeaf[n]; }; FindNode: PUBLIC --ENTRY-- PROC [someName: ROPE, class: NodeClass] RETURNS [node: Node] = { FNWork: --INTERNAL-- PROC = { node _ GetNode[someName, class, FALSE]; IF node # NIL THEN RETURN; node _ TryToProduce[someName, class]; InnerVolunteerLeaf[node]; }; DoIn[FNWork]; }; Found: ERROR = CODE; TryToProduce: --INTERNAL-- PROC [resultName: ROPE, class: NodeClass] RETURNS [sought: Node] = BEGIN PerFinder: --INTERNAL-- PROC [f: Finder] = { found: BOOLEAN; makes, cmdFrom: NodeList; from: From; cmd: ROPE; class: ActionClass; foundData: REF ANY; CedarProcess.CheckAbort[]; [found, sought, makes, cmdFrom, from, cmd, class, foundData] _ f.finderProc[resultName: resultName, finderData: f.finderData]; IF found THEN { first: BOOLEAN _ TRUE; soughtIn: BOOL _ FALSE; already: Action _ NIL; FOR ml: NodeList _ makes, ml.rest WHILE ml # NIL DO this: Action _ IF ml.first.producer = NIL THEN NIL ELSE ml.first.producer.a; soughtIn _ soughtIn OR (sought = ml.first); IF first THEN {already _ this; first _ FALSE} ELSE {IF this # already THEN Warning[IO.PutFR[ "Action %g doesn't precisely cover command %g (e.g., at %g)", IO.refAny[cmd], IO.refAny[IF already # NIL THEN already.cmd ELSE NIL], IO.rope[ml.first.name]]]}; ENDLOOP; IF soughtIn THEN { a: Action; IF already # NIL THEN ERROR Found; a _ NEW [ActionRep _ [cmd: cmd, class: class, foundData: foundData]]; InnerAddAction[a, makes, cmdFrom, from]; ERROR Found } ELSE { Warning[Rope.Cat["Finder (", f.name, ") blew it"]]; sought _ NIL; }; } ELSE sought _ NIL; }; sought _ NIL; EnumerateFinders[PerFinder !Found => CONTINUE]; IF sought=NIL THEN sought _ GetNode[resultName, class]; END; InnerAddAction: --INTERNAL-- PROC [a: Action, makes, cmdFrom: NodeList, from: From] = { FOR nl: NodeList _ makes, nl.rest WHILE nl # NIL DO n: Node _ nl.first; peh: EdgeRingHead _ emptyHead; IF n.producer # NIL THEN ERROR; IF n.current # notComputed THEN ERROR; [a.makes, peh] _ Link[a, a.makes, n, peh, FALSE]; n.producer _ peh.first; ENDLOOP; InnerAddConsumption[a, cmdFrom, cmd, TRUE]; InnerAddConsumption[a, from.mustHave, data, FALSE]; InnerAddConsumption[a, from.optional, data, TRUE]; }; VolunteerLeaf: --ENTRY-- PROC [n: Node] = { VLWork: --INTERNAL-- PROC = {InnerVolunteerLeaf[n]}; DoIn[VLWork]; }; InnerVolunteerLeaf: --INTERNAL-- PROC [n: Node] = { IF n.producer = NIL THEN n.producer _ NEW [EdgeRep _ [ a: leaf, n: n, aNext: NIL, aPrev: NIL, nNext: NIL, nPrev: NIL, optional: FALSE ]] ELSE IF n.producer.a = NIL THEN ERROR; }; InnerRederive: PUBLIC --INTERNAL-- PROC [a: Action] = { from: From; InnerRemoveConsumption[a, data]; [from, a.cmd] _ a.class.Rederive[a]; InnerAddConsumption[a, from.mustHave, data, FALSE]; InnerAddConsumption[a, from.optional, data, TRUE]; }; AddConsumption: --ENTRY-- PROC [a: Action, nl: NodeList, which: ActionDep, optional: BOOL] = { ACWork: --INTERNAL-- PROC = {InnerAddConsumption[a, nl, which, optional]}; DoIn[ACWork]; }; InnerAddConsumption: --INTERNAL-- PROC [a: Action, nl: NodeList, which: ActionDep, optional: BOOL] = { FOR nl _ nl, nl.rest WHILE nl # NIL DO n: Node _ nl.first; SELECT which FROM data => { [a.from[data], n.to[data]] _ Link[a, a.from[data], n, n.to[data], optional]; }; cmd => { [a.from[cmd], n.to[cmd]] _ Link[a, a.from[cmd], n, n.to[cmd], optional]; }; ENDCASE => ERROR; ENDLOOP; nl _ nl; }; Link: --INTERNAL-- PROC [a: Action, ah: EdgeRingHead, n: Node, nh: EdgeRingHead, optional: BOOL] RETURNS [ahNew, nhNew: EdgeRingHead] = { e: Edge _ NEW [EdgeRep _ [ optional: optional, n: n, a: a, aNext: ah.first, aPrev: NIL, nNext: nh.first, nPrev: NIL ]]; ah.first _ nh.first _ e; IF e.aNext # NIL THEN e.aNext.aPrev _ e ELSE ah.last _ e; IF e.nNext # NIL THEN e.nNext.nPrev _ e ELSE nh.last _ e; ahNew _ ah; nhNew _ nh; }; InnerRemoveConsumption: --INTERNAL-- PROC [a: Action, which: ActionDep] = { ah: EdgeRingHead _ a.from[which]; aNext: Edge; FOR e: Edge _ ah.first, aNext WHILE e # NIL DO n: Node _ e.n; IF e.a # a THEN ERROR; aNext _ e.aNext; e.aNext _ e.aPrev _ NIL; UnlinkToFrom[e, which]; e.a _ NIL; e.n _ NIL; ENDLOOP; a.from[which] _ emptyHead; }; UnlinkToFrom: --INTERNAL-- PROC [e: Edge--on n.to=a.from--, which: ActionDep] = { n: Node = e.n; IF e.nNext # NIL THEN e.nNext.nPrev _ e.nPrev ELSE SELECT which FROM cmd => n.to[cmd].last _ e.nPrev; data => n.to[data].last _ e.nPrev; ENDCASE => ERROR; IF e.nPrev # NIL THEN e.nPrev.nNext _ e.nNext ELSE SELECT which FROM cmd => n.to[cmd].first _ e.nNext; data => n.to[data].first _ e.nNext; ENDCASE => ERROR; e.nNext _ e.nPrev _ NIL; }; InnerRemoveProduction: --INTERNAL-- PROC [a: Action] = { ah: EdgeRingHead _ a.makes; aNext: Edge; FOR e: Edge _ ah.first, aNext WHILE e # NIL DO n: Node _ e.n; IF e.a # a THEN ERROR; aNext _ e.aNext; e.aNext _ e.aPrev _ NIL; e.nNext _ e.nNext _ NIL; n.producer _ NIL; e.a _ NIL; e.n _ NIL; ENDLOOP; a.makes _ emptyHead; }; Explain: PUBLIC PROC [ch: Commander.Handle, nodes: RefTable] = { to: IO.STREAM = ch.out; Work: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { n: Node = NARROW[data]; ExplainNode[n, to]; }; StatelessEnumerateRefTable[nodes, Work]; }; ExplainNode: --ENTRY-- PROC [n: Node, to: IO.STREAM] = { ShowConsumer: --INTERNAL-- PROC [a: Action, which: ActionDep] = { to.PutF["\n\t\t%g", [rope[a.cmd]]]; }; ShowDeterminer: --INTERNAL-- PROC [n: Node, which: ActionDep, optional: BOOL] = { nc: BOOL = n.current # true; broken: BOOL = n.current=true AND n.broken; to.PutF["\n%g\t%g\t%g", [rope[IF nc THEN "*" ELSE IF broken THEN "!" ELSE ""]], [rope[SELECT InnerGetCreated[n] FROM notExistTime => "?", unknownTime => "??", ENDCASE => ""]], IO.rope[n.name] ]; }; ShowInput: --INTERNAL-- PROC [n: Node, which: ActionDep, optional: BOOL] = { nc: BOOL = n.current # true; broken: BOOL = n.current=true AND n.broken; to.PutF["\n%g\t%g\t%g%g", [rope[IF nc THEN "*" ELSE IF broken THEN "!" ELSE ""]], [rope[SELECT InnerGetCreated[n] FROM notExistTime => "?", unknownTime => "??", ENDCASE => ""]], IO.rope[n.name], IO.rope[IF optional THEN "?" ELSE ""] ]; }; ShowOutput: PROC [n: Node] = { to.PutF["\n\t\t%g", IO.rope[n.name]]; }; ENWork: --INTERNAL-- PROC = { a: Action _ NIL; to.PutF["%g\n", [rope[n.name]] ]; to.PutF["\tCreated %g.\n", [rope[FmtTime[n.created]]] ]; to.PutF["\t%g\n", [rope[SELECT n.current FROM true => "Is Current", false => "Not Current", notComputed => "Not known if Current", ENDCASE => ERROR]]]; IF n.current=true THEN to.PutF["\t%g.\n", [rope[IF n.broken THEN "Broken" ELSE "Not broken"]] ]; to.PutRope["\tNeeded by {"]; InnerEnumerateConsumers[n, data, ShowConsumer]; to.PutRope["};\n\tdetermines {"]; InnerEnumerateConsumers[n, cmd, ShowConsumer]; to.PutRope["}.\n"]; SELECT TRUE FROM n.producer = NIL => to.PutRope["\tNever tried to determine producer.\n"]; n.producer.a = leaf => to.PutRope["\tNothing knows how to produce it.\n"]; ENDCASE => a _ n.producer.a; SELECT n.modifiability FROM yes => to.PutRope["\tCertainly modifiable.\n"]; no => to.PutRope["\tCertainly NOT modifiable.\n"]; guess => to.PutRope["\tModifiability unspecified.\n"]; uninitialized => to.PutRope["\tModifiability undetermined.\n"]; ENDCASE => ERROR; IF (a = NIL) OR (n.modifiability = no) THEN RETURN; IF NOT n.consistencyAsked THEN to.PutRope["\tConsistency not inquired.\n"] ELSE to.PutF[ "\tCurrently %g consistent with inputs (because %g).\n", [rope[IF n.consistent THEN "is" ELSE "not"]], [rope[n.consistencyReason]] ]; to.PutF["%g\n", [rope[a.cmd]] ]; to.PutRope["\tAccording to {"]; InnerEnumerateSources[a, cmd, ShowDeterminer]; to.PutRope["}\n\tMakes {"]; InnerEnumerateResults[a, ShowOutput]; to.PutRope["}\n\tFrom {"]; InnerEnumerateSources[a, data, ShowInput]; to.PutRope["}.\n"]; to.PutF["\t%g from current determiners.\n", [rope[IF a.derivedFromCurrentDeterminers THEN "Derived" ELSE "Not derived"]] ]; to.PutF["\t%g with current inputs.\n", [rope[SELECT TRUE FROM a.queuedFailsInvalidation OR a.fails=notComputed => "Not tried", a.fails=true => "Fails", a.fails=false => "Succeeds", ENDCASE => ERROR]] ]; IF a.reasonWhyLastDone # NIL THEN to.PutF["\tLast executed because %g.\n", [rope[a.reasonWhyLastDone]]]; }; DoIn[ENWork]; }; InnerLeaf: PUBLIC --INTERNAL-- PROC [n: Node] RETURNS [isLeaf: BOOL] = { SELECT n.modifiability FROM no => RETURN [TRUE]; yes, guess => NULL; uninitialized => ERROR; ENDCASE => ERROR; isLeaf _ n.producer = NIL OR n.producer.a = leaf; }; SuspectNodeChange: PUBLIC --ENTRY-- PROC [n: Node] = { SNCWork: --INTERNAL-- PROC = {InnerSuspectNodeChange[n]}; DoIn[SNCWork]; }; InnerSuspectNodeChange: --INTERNAL-- PROC [n: Node] = { lastCreated: Time = n.created; n.created _ n.class.GetTime[n]; IF n.created # lastCreated THEN InnerNoteContentChange[n]; }; reasonNotAsked: ROPE = "haven't yet asked if consistent"; InnerNoteContentChange: --INTERNAL-- PROC [n: Node] = { InnerUnaskConsistency: --INTERNAL-- PROC [n: Node] = { n.consistencyAsked _ FALSE; n.consistencyReason _ reasonNotAsked; }; PerDirectConsumer: --INTERNAL-- PROC [a: Action, which: ActionDep] = { SELECT which FROM cmd => { a.derivedFromCurrentDeterminers _ FALSE; }; data => { InnerUnknowFails[a]; InnerEnumerateResults[a, InnerUnaskConsistency]; }; ENDCASE => ERROR; }; InnerUnaskConsistency[n]; InnerEnumerateConsumers[n, data, PerDirectConsumer]; InnerEnumerateConsumers[n, cmd, PerDirectConsumer]; InnerUncurrentNode[n]; n _ n; }; InnerUnknowFails: --INTERNAL-- PROC [a: Action] = { SELECT a.permissionGranted FROM FALSE => a.fails _ notComputed; TRUE => a.queuedFailsInvalidation _ TRUE; ENDCASE => ERROR; }; UncurrentNode: PUBLIC --ENTRY-- PROC [n: Node] = { UNWork: --INTERNAL-- PROC = {InnerUncurrentNode[n]}; DoIn[UNWork]; }; InnerUncurrentNode: --INTERNAL-- PROC [n: Node] = { PerConsumer: --INTERNAL-- PROC [a: Action, which: ActionDep] = { InnerEnumerateResults[a, InnerUncurrentNode]; }; IF n.current = notComputed THEN RETURN; SetCurrency[n, notComputed]; InnerEnumerateConsumers[n, data, PerConsumer]; InnerEnumerateConsumers[n, cmd, PerConsumer]; n _ n; }; UncurrentProducer: PUBLIC --ENTRY-- PROC [n: Node] = { UPWork: --INTERNAL-- PROC = {InnerUncurrentProducer[n]}; DoIn[UPWork]; }; InnerUncurrentProducer: --INTERNAL-- PROC [n: Node] = { IF n.producer = NIL OR n.producer.a = leaf THEN InnerUncurrentNode[n] ELSE { a: Action = n.producer.a; InnerUnknowFails[a]; InnerEnumerateResults[a, InnerUncurrentNode]; }; }; EnumerateConsumers: PUBLIC --ENTRY-- PROC [n: Node, which: ActionDep, to: PROC [Action, ActionDep]] = { ECWork: --INTERNAL-- PROC = {InnerEnumerateConsumers[n, which, to]}; DoIn[ECWork]; }; InnerEnumerateConsumers: --INTERNAL-- PROC [n: Node, which: ActionDep, to: PROC [Action, ActionDep]] = { x: BOOL _ FALSE; x _ x; FOR e: Edge _ n.to[which].first, e.nNext WHILE e # NIL DO to[e.a, which]; ENDLOOP; x _ x; }; EnumerateResults: PUBLIC --ENTRY-- PROC [a: Action, to: PROC [Node]] = { ERWork: --INTERNAL-- PROC = {InnerEnumerateResults[a, to]}; DoIn[ERWork]; }; InnerEnumerateResults: --INTERNAL-- PROC [a: Action, to: PROC [Node]] = { x: BOOL _ FALSE; x _ x; FOR e: Edge _ a.makes.first, e.aNext WHILE e # NIL DO to[e.n]; ENDLOOP; x _ x; }; EnumerateSources: PUBLIC --ENTRY-- PROC [a: Action, which: ActionDep, to: PROC [n: Node, which: ActionDep, optional: BOOL]] = { ESWork: --INTERNAL-- PROC = {InnerEnumerateSources[a, which, to]}; DoIn[ESWork]; }; InnerEnumerateSources: PUBLIC --INTERNAL-- PROC [a: Action, which: ActionDep, to: PROC [n: Node, which: ActionDep, optional: BOOL]] = { which _ which; FOR e: Edge _ a.from[which].first, e.aNext WHILE e # NIL DO IF e.a # a THEN ERROR; to[e.n, which, e.optional]; ENDLOOP; which _ which; }; GetCreated: PUBLIC --ENTRY-- PROC [n: Node] RETURNS [t: Time] = { GCWork: --INTERNAL-- PROC = {t _ n.created}; DoIn[GCWork]; }; InnerGetCreated: PUBLIC --INTERNAL-- PROC [n: Node] RETURNS [t: Time] = { t _ n.created; }; StartTime: PUBLIC PROC [n: Node] = { n.created _ n.class.GetTime[n]; }; DestroyGraph: PUBLIC --ENTRY-- PROC = { DGWork: --INTERNAL-- PROC = { DestroyNode: --INTERNAL-- PROC [n: Node] = { IF n.producer # NIL AND n.producer.a # leaf THEN { a: Action = n.producer.a; a.class _ NIL; a.foundData _ NIL; InnerRemoveConsumption[a, cmd]; InnerRemoveConsumption[a, data]; InnerRemoveProduction[a]; }; }; IF workCount > 0 THEN Warning["Hope no other processes in MakeDo..."]; EnumerateNodes[to: DestroyNode, andDestroy: TRUE]; readyQueue _ NIL; workCount _ 0; waitQueue.DestroyTable[]; halt _ FALSE; }; DoIn[DGWork]; }; ForAll: PUBLIC --ENTRY-- PROC [suspectChange, uncurrent: BOOL] = { FAWork: --INTERNAL-- PROC = { PerNode: INTERNAL PROC [n: Node] = { IF suspectChange THEN InnerSuspectNodeChange[n]; IF uncurrent THEN InnerUncurrentProducer[n]; }; EnumerateNodes[to: PerNode, andDestroy: FALSE]; }; DoIn[FAWork]; }; RetryToProduce: PUBLIC --ENTRY-- PROC [n: Node] = { RPWork: --INTERNAL-- PROC = { n2: Node; IF n.producer # NIL AND n.producer.a # leaf THEN RETURN; IF n.producer # NIL AND n.producer.a = leaf THEN { e: Edge = n.producer; IF e.aNext # NIL OR e.aPrev # NIL THEN ERROR; IF e.nNext # NIL OR e.nPrev # NIL THEN ERROR; n.producer.a _ NIL; n.producer _ NIL; InnerUncurrentNode[n]; }; n2 _ TryToProduce[n.name, n.class]; IF n # n2 THEN Warning[IO.PutFR["Disagreement on cannonical name for %g or %g", IO.rope[n.name], IO.rope[n2.name]]]; InnerVolunteerLeaf[n]; }; DoIn[RPWork]; }; EnsureWDir: PROC [wDir: ROPE, parent: Commander.Handle] = { IF NOT CommandTool.CurrentWorkingDirectory[].Equal[wDir, FALSE] THEN { [] _ CommandTool.DoCommand[Rope.Cat["CD ", wDir], parent]; IF NOT CommandTool.CurrentWorkingDirectory[].Equal[wDir, FALSE] THEN ERROR; }; }; FindCycles: --INTERNAL-- PROC = { out: IO.STREAM = GetCommanderHandle[].out; explored: RefTable _ MakeRefTable[]; inStack: RefTable _ MakeRefTable[]; stack: NodeList _ NIL; cycleCount: INTEGER _ 0; PerElement: PROC [n: Node] = { out.PutF["\t%g\n", [rope[n.name]] ]; InnerWakeWaiters[n]; IF n.producer#NIL AND n.producer.a#leaf THEN InnerWakePermissionWaiters[n.producer.a]; }; NoteNode: --INTERNAL-- PROC [n: Node] = { IF explored.Lookup[n] # NIL THEN RETURN; IF inStack.Lookup[n] # NIL THEN { cycleCount _ cycleCount + 1; out.PutRope["Found cycle:\n"]; PerElement[n]; FOR nl: NodeList _ stack, nl.rest WHILE nl.first # n DO PerElement[nl.first]; ENDLOOP; out.PutRope["\n"]; RETURN; }; inStack.Insert[n, n]; stack _ CONS[n, stack]; FOR wl: NodeList _ n.waitingOnCurrent, wl.rest WHILE wl # NIL DO [] _ NoteNode[wl.first]; ENDLOOP; IF n.producer # NIL AND n.producer.a # leaf THEN { a: Action = n.producer.a; FOR wl: NodeList _ a.waitingForPermission, wl.rest WHILE wl # NIL DO [] _ NoteNode[wl.first]; ENDLOOP; }; stack _ stack.rest; IF DeleteFromRefTable[n, inStack] # n THEN ERROR; explored.Insert[n, n]; }; halt _ TRUE; Warning["MakeDo deadlocked... hang on while I look for dependency cycles... "]; FOR n: Node _ NARROW[waitQueue.LookupSmallest[]], NARROW[waitQueue.LookupNextLarger[n]] WHILE n # NIL DO NoteNode[n]; ENDLOOP; Warning[IO.PutFR["... found %g cycle(s) ... halting", [integer[cycleCount]] ]]; }; StatelessEnumerateRefTable: PUBLIC PROC [table: RefTable, EachNode: RedBlackTree.EachNode] = { table _ table; FOR ref: REF ANY _ table.LookupSmallest[], table.LookupNextLarger[ref] WHILE ref # NIL DO IF EachNode[ref] THEN ERROR; ENDLOOP; table _ table; }; Start: PROC = { TRUSTED { mcp: Process.ConditionPointer = @monitorChange; qcp: Process.ConditionPointer = @queueChange; Process.InitializeCondition[mcp, Process.SecondsToTicks[entryTimeout]]; Process.EnableAborts[mcp]; Process.InitializeCondition[qcp, Process.SecondsToTicks[queueTimeout]]; Process.EnableAborts[qcp]; }; UserProfile.CallWhenProfileChanges[SetProcessAllocationFromProfile]; }; Start[]; END. ��¼��MakeDoBasicImpl.Mesa Last Edited by: Spreitzer, May 9, 1986 8:38:29 pm PDT Carl Hauser, April 11, 1985 3:43:34 pm PST INVARIANT in => some process in Basic monitor. condition variables. Only one process writing to a commander log at a time. Integrity of MakeDoAuxBox data structure. Additional invariants of the Basic monitor: Integrity of queue reps. Node is in queue node.state says it is. node.current=true => node not ready or waiting. node.current#true & someone wants it true => node in some queue. processUse & processAllocation are accurate. halt => make nodes inactive regardless of currency. halt => queues not all empty. To help debugging: We don't use Mesa's monitor entry mechanism only because we can't ABORT something waiting on entry --- what a pisser! With potentially non-empty queues: With permission to work on toExecute and goal: either toExecute = NIL & no work to be done or { toExecute#NIL not consistent fails = toExecute.fails fails # true } Does anything need to be put back in the queue? Would you believe... I'm thinking of not surprising the other UNWIND catch phrase cooler in the stack. With work lock on goal: With permission to work on a: Would you believe... I'm thinking of not surprising the other UNWIND catch phrase cooler in the stack. We don't have to notify anybody because we wouldn't be here unless NOT CorrectlyDerived[a], which implies all upstream stuff is suspicious. GetActions: PUBLIC --ENTRY-- PROC [goals: RefTable, modifiabilitySpec: ModifiabilitySpec] RETURNS [al: ActionList] = { GsWork: --INTERNAL-- PROC = { asTable: RefTable _ MakeRefTable[]; PerNode: INTERNAL PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { n: Node = NARROW[data]; a: Action; InnerSetModifiability[n, goals, modifiabilitySpec]; IF n.producer = NIL OR n.producer.a = leaf OR n.modifiability=no THEN RETURN; a _ n.producer.a; IF asTable.Lookup[a] = a THEN RETURN; asTable.Insert[a, a]; InnerEnumerateSources[a, cmd, PerSource]; InnerEnumerateSources[a, data, PerSource]; al _ CONS[a, al]; }; PerSource: INTERNAL PROC [n: Node, which: ActionDep, optional: BOOL] = { [] _ PerNode[n]; }; StatelessEnumerateRefTable[goals, PerNode]; asTable.DestroyTable[]; }; DoIn[GsWork]; }; �Ê.Ñ��– "cedar" style˜�code™J™5K™*—K˜�KšÏk œFœ œË˜§K˜�šÐbxœœ˜Kšœ/œŸ˜×Kšœ$˜+™ K™$K™K™6K™)—™+K™K™'K™/K™@Kšœ,™,K™3K™——K˜�Kšœ˜Kšœœ˜!K˜�Kšœ œœ˜-Kšœœœ˜1Kšœœœ˜7K˜�Kšœœ˜$Kšœœœœ˜(Kšœ œœœ˜2K˜�Kšœœœ˜™Kšœœœ˜Kšœœ˜ —Kšœ œ˜Kšœœ˜K˜�K™uK˜�šÏnœœœœ˜K˜K˜ Kšœœ˜K˜K˜—K˜�šŸœœœœ˜Kšœœœ˜Kšœœœœ˜'Kšœœ˜ K˜Kšœ˜K˜—K˜�šŸœœœ˜Kšœœœ˜Kšœœ˜Kšœ œ˜ Kšœ œ˜ Kšœ˜K˜—K˜�Kšœœ˜Kšœœ˜K˜�š ŸœœÏc œœœ˜8šŸœ œœ˜Kšœ˜K˜K˜—K˜K˜—K˜�šŸœœ+ "œ˜všœ˜šœ˜Kšœœ?˜FKšœ˜K˜—˜ K˜K˜—Kšœœ˜—K˜—K˜�šŸœ œœ˜šŸœ œœ˜#Kšœ˜K˜Kšœ˜—Kšœ˜K˜—K˜�Kšœœœ˜Kšœœ˜Kšœœ˜K˜%Kšœ œ˜Kšœœ˜K˜�šŸœœœ˜Kšœœœ˜Kš œ ˜K˜—K˜�šŸœœœ˜Kšœœœ˜Kšœ ˜K˜—K˜�šŸ œœœ˜Kšœœœ˜Kšœ ˜K˜—K˜�šŸœ œœ˜9Kšœœœ˜šœ ˜Kšœœ˜K˜&K˜%Kšœœ&œœ˜>Kšœœ˜—K˜šœ ˜Kšœœ˜˜ Kšœ œ˜!K˜—K˜%˜Kšœ˜K˜—Kšœœ˜—K˜K˜—K˜�šŸœ œœ)œ˜Pšœœ˜Kšœ˜K˜K˜—šœœœ˜Kšœœ"˜;Kšœ2˜2šœ˜Kšœœ˜Kšœ*˜*Kšœ˜Kšœœ˜—K˜—K˜—K˜�šŸœ œœ%˜Mšœœœ˜"K˜Kšœ˜K˜Kšœœœœ˜HK˜—K˜—K˜�šŸœ œœ˜0šŸœ œœ˜šŸœœœœœœœ œ˜\Kšœ œ˜Kšœœœœ˜-K˜—Kšœ2˜2K˜Kšœ˜K˜—Kšœ˜K˜—K˜�š Ÿ œ œœÏgœœ˜?šœ˜Kšœ8¡œ˜:˜ Kšœ¡œ˜šœ ˜Kšœ¡œ˜Kšœ¡œ˜"Kšœœ˜—Kšœ˜—Kšœœ˜—K˜—K˜�šŸœœ œœ&˜KšŸœœœœœœœ œ˜YKšœœ˜Kšœ˜K˜—šŸœœœœœœœ œ˜VKšœœ˜Kšœ˜K˜—Kšœœœ˜#Kšœ4˜4K˜Kšœ1˜1K˜—K˜�š Ÿ œœ œœœ˜?šŸœœœœœœœ œ˜YKšœœ˜Kšœ˜K˜—šŸœœœœœœœ œ˜VKšœœ˜Kšœ˜K˜—Kšœœœ˜!Kšœ4˜4Kšœ˜Kšœ1˜1K˜—K˜�šŸœ œœ˜3Kšœ˜Kšœœœ˜#K˜K˜—K˜�šŸœ œœ˜'KšŸœ œœ˜0K˜ K˜—K˜�šŸœ œœ˜/šœœ˜K˜Kšœœ˜Kšœ˜K˜—Kšœœœ œœœ˜KK˜—K˜�šŸœ œœ˜1šœœ˜!Kšœ(˜(Kšœ-˜-Kšœœœ˜%K˜(šœ˜Kšœœ˜K˜Kšœœ˜Kšœœ˜—Kšœ˜—K˜—K˜�š ŸœœœSœ'œ7˜Îšœœ˜!Kšœ,˜,K˜ Kšœ˜Kšœ"˜"K˜—šœœ˜)K˜K˜—šŸœœœœœœœ œ˜YKšœ œ˜šŸœ œœ˜KšœG˜GK˜-K˜—K˜K˜—™"šœœœ˜K˜K˜—Kšœ,˜,Kšœv˜vK˜$K˜—K˜Kšœ%˜%K˜Kšœ˜K˜—K˜�šŸœ œœ˜$šŸœ œœ˜Kšœœœ˜š œœœ œœ˜=Kšœœ˜šœœ˜K˜ K˜K˜Kšœ˜—K˜K˜—K˜—K˜K˜—K˜�šŸœœ>œœ˜™Kšœ˜š˜šœœ˜šŸœœœœœœœ œ˜WKšœ œ˜-Kšœ"˜)K˜—Kšœ9˜9K˜—Kšœœ˜Kšœ œ˜Kšœœ˜Kšœœœ˜K˜Kšœœ˜Kšœ§˜§Kšœ œœœ˜šœ.™.šœœœ˜Kšœ˜K˜ Kšœœ˜K˜—KšœœI˜[Kšœ0˜0Kšœœ œ.˜NKšœœ˜#šœ˜šœ˜Kšœ˜KšœœL˜XKšœ>œ˜EKšœ1˜1Kšœ˜—šœ˜Kšœ˜Kšœ˜——K˜—Kšœ˜—Kšœ˜K˜—K˜�Kšœœœ˜'Kš œœœFœ"œ˜K˜�šŸœœœœœœœœ œ˜`šœœ˜Kšœœ˜šœœ˜Kšœ˜K˜ Kšœ œ˜K˜—Kšœœ˜ Kšœœœœœ+œ*œ˜„Kšœ˜Kšœ6˜6Kšœœ˜Kšœ˜Kšœœ˜$Kšœ,˜,K˜!K˜—Kšœ œ˜K˜—K˜�Kšœœ˜Kšœœ˜&KšœH˜HKšœœ˜Kšœœ˜Kšœœ˜K˜K˜�šŸœœœœ˜MKšœœœ˜Kšœœœ ˜1K˜K˜ šœ œœœ˜*Kšœ*œ:˜hKšœC˜CK˜Kšœ˜K˜—˜K˜NKšœœ˜ —Kšœ#˜#K˜šœœ˜&K˜Kšœm˜mK˜—K˜ZKšœI˜IKšœœ˜Kšœœœœ˜VKšœ˜K˜—K˜�šŸœœœ;œœ œœ œ˜•Kšœœœ˜šœœœ˜Kšœ{œ˜K˜Kšœœ˜ K˜—K˜—K˜�š Ÿœœœ œœ˜aKšœœœ˜K˜(K˜—K˜�šŸœœœ ˜1Kšœœœ˜Kšœœœœ'˜AKšœ œ&˜9Kšœœ˜Kšœ)œ˜/šœ œœœ˜*K˜Kšœ˜K˜—šœ)œœ˜>šœœ˜Kšœ˜Kšœœœœ˜Hšœœ˜Kšœœœœ˜Kšœ˜Kšœ˜—Kšœœ˜#šœ$œœ˜:Kšœ$˜$Kšœ œ'˜:Kšœ8œ˜?Kšœ˜Kšœ˜—Kšœ&˜&Kšœ˜K˜—K˜Kšœ˜—K˜—K˜�šŸœ œœ6˜VKšŸœ œœ)˜BK˜ K˜—K˜�šŸœœ œœ6˜ešœœœ˜%K˜Kšœœ ˜šœœ˜#Kšœ ˜ Kšœ˜ Kšœœ˜——Kšœœœ˜%Kšœ˜K˜K˜K˜—K˜�šŸœ œœ>œ,œœ%œœœ!œ˜¬™Kšœ$™$šœ™Kšœ ™ K™Kšœ™K™K™——šŸœ œœ˜Kšœ ˜ š˜K˜ Kšœœœ˜Kšœœœ˜Kšœœœ˜Kšœœ˜šŸœ œœ'œ˜NK˜Kšœ9˜9šœœœœ˜5Kšœœ˜Kšœœœ˜@K˜—šœ˜šœ ˜ Kšœ œ œ˜6Kšœ˜Kšœ˜—Kšœ"˜"Kšœœ˜Kšœœ˜—K˜Kšœœ˜ K˜—Kš œœœœœ˜2š˜K˜šœœ˜šŸ œœœœœœœ œ˜[Kšœ œ˜šœœœ ˜"šœ˜Kšœ˜K˜—šœ˜Kšœ$˜$Kšœœ˜'K˜——K˜—Kšœ!˜!Kšœœ˜Kšœ5˜5Kšœ˜—Kšœœœœ˜šœœ˜šœœ˜K˜ Kšœ˜—™/Kšœ˜šŸœœœœœœœ œ˜]Kšœ œ˜Kšœœœ˜!Kšœœœ˜8K˜—Kšœ7˜7Kšœœœœ˜Kšœ˜K˜—Kšœ˜—K˜šœ˜™šœ˜K™Q——Kšœ˜—K˜Kšœ˜—K˜Kšœœœ˜!K˜Kšœœœ˜'™Kšœœœ˜$Kšœ œœœ˜1Kšœ˜Kšœœœ )œ˜JKšœœ)˜:Kšœ'œ˜FKšœœœ˜3Kšœœœ˜6K˜šœœ˜Kšœœ˜<K˜K˜Kšœ˜—Kšœœ˜Kšœœ˜"™Kšœœœ˜+Kšœœ%˜6K˜+Kšœœœ˜/Kšœœ3œ˜Mšœ!˜+Kšœœ˜ šœ˜ K˜Kšœ"œ˜'Kšœ˜—Kšœœ˜—Kšœœ˜K˜,Kšœœœ˜/Kšœœ3œ˜Mšœœ˜šœœ˜ Kšœœ˜šœœ˜Kšœ%˜%K˜K˜"K˜—Kšœ˜—Kšœ˜Kšœ˜Kšœ˜—šœ˜Kšœœ4œ˜WKšœœ˜Kšœœ˜—šœœœ˜#KšœN˜NKšœœ˜K˜—Kšœ+˜+Kšœœ3œ˜PK˜K˜š˜šœ˜Kšœœœ˜Kšœœœ˜>šœ˜K˜šœ˜™šœ˜K™Q——Kšœ˜—K˜K˜——Kšœ˜—Kšœ˜K˜—K˜—Kšœ˜—K˜—Kšœœ˜K˜K˜—K˜�šŸœ œœL˜cšŸœ œœ˜Kšœœœ˜#K˜Kšœ ˜ Kšœœœ$˜9Kšœ˜—K˜ K˜—K˜�šŸœ œœ˜0KšŸœ œœ˜7K˜ K˜—K˜�šŸœ œœ˜8Kšœœ˜Kšœœ˜8Kšœœ˜"Kšœ˜K˜—K˜�šŸœ œœ˜=K˜šœœ˜%Kšœ,˜,Kšœ5˜5Kšœœœ˜%Kšœœœ˜#K˜K˜Kšœ˜—K˜K˜—K˜�šŸœœ œœ˜8K˜ Kšœœœœ˜šœ˜Kšœœ Bœ˜XKšœœ˜Kšœœ˜—Kšœ#˜#Kš œœ œ7œœ˜tKšœ˜Kšœ˜—K˜�šŸœœ œœœœ˜[šŸœ œœ˜Kšœ œ˜'Kšœœœœ˜Kšœ%˜%K˜K˜—K˜ K˜—K˜�Kšœœœ˜K˜�š Ÿœ œœœœ˜]Kš˜šŸ œ œœ˜,Kšœœ˜K˜K˜Kšœœ˜ J˜Kšœœœ˜K˜Kšœ~˜~šœœ˜Kšœœœ˜Kšœ œœ˜Kšœœ˜šœœœ˜3Kšœœœœœœ˜LKšœœ˜+šœ˜Kšœœ˜$šœœ˜šœ œ˜Kšœ=˜=Kšœ ˜Kšœœœœ œœ˜6Kšœ˜———Kšœ˜—šœ ˜šœ˜K˜ Kšœœœœ˜"Kšœœ>˜EKšœ(˜(Kšœ˜K˜—šœ˜Kšœ3˜3Kšœ œ˜ Kšœ˜——Kšœ˜—Kšœ œ˜K˜—Kšœ œ˜ Kšœ%œ˜/Kšœœœ%˜7Kšœ˜—K˜�šŸœ œœ6˜Wšœœœ˜3K˜Kšœ˜Kšœœœœ˜Kšœœœ˜&Kšœ*œ˜1Kšœ˜Kšœ˜—Kšœ%œ˜+Kšœ,œ˜3Kšœ,œ˜2K˜—K˜�šŸ œ œœ˜+KšŸœ œœ˜4K˜ K˜—K˜�šŸœ œœ˜3šœœœœ ˜6K˜K˜Kš œœ œ œ œ˜/Kšœ ˜K˜—Kš œœœœœ˜&K˜—K˜�šŸ œœ œœ˜7K˜K˜ K˜$Kšœ,œ˜3Kšœ,œ˜2K™‹K˜—K˜�šŸœ œœ7œ˜^KšŸœ œœ1˜JK˜ K˜—K˜�šŸœ œœ7œ˜fšœœœ˜&K˜šœ˜šœ ˜ KšœL˜LKšœ˜—šœ˜KšœH˜HKšœ˜—Kšœœ˜—Kšœ˜—K˜K˜—K˜�š Ÿœ œœDœœ!˜‰šœ œ ˜Kšœ˜K˜K˜K˜Kšœœ˜K˜Kšœ˜ K˜—K˜Kšœœœœ ˜9Kšœœœœ ˜9K˜K˜K˜—K˜�šŸœ œœ"˜KKšœ!˜!K˜šœœœ˜.K˜Kšœ œœ˜K˜Kšœœ˜Kšœ˜Kšœœ˜ Kšœœ˜ Kšœ˜—Kšœ˜K˜—K˜�šŸœ œœ œ˜QK˜KšœœœœœœEœœ˜šKšœœœœœœGœœ˜œKšœœ˜K˜—K˜�šŸœ œœ˜8Kšœ˜K˜šœœœ˜.K˜Kšœ œœ˜K˜Kšœœ˜Kšœœ˜Kšœ œ˜Kšœœ˜ Kšœœ˜ Kšœ˜—Kšœ˜K˜—K˜�šŸœœœ,˜@Kšœœœ ˜šŸœœœœœœœ œ˜UKšœ œ˜Kšœ˜K˜—Kšœ(˜(K˜—K˜�š Ÿœ œœœœ˜8šŸœ œœ"˜AKšœ#˜#Kšœ˜—šŸœ œœ'œ˜QKšœœ˜Kšœœœ ˜+šœ˜Kš œœœœœœœ˜7šœœ˜$Kšœ˜Kšœ˜Kšœ ˜—Kšœ ˜Kšœ˜—Kšœ˜—šŸ œ œœ'œ˜LKšœœ˜Kšœœœ ˜+šœ˜Kš œœœœœœœ˜7šœœ˜$Kšœ˜Kšœ˜Kšœ ˜—Kšœ˜Kšœœ œœ˜%Kšœ˜—Kšœ˜—šŸ œœ˜Kšœœ˜%Kšœ˜—šŸœ œœ˜Kšœœ˜K˜!K˜8šœœ˜-K˜K˜K˜&Kšœœ˜—šœœ˜)Kšœœ œ œ˜3Kšœ˜—K˜Kšœ/˜/K˜!Kšœ.˜.Kšœ˜šœœ˜Kšœ œ9˜IK˜JKšœ˜—šœ˜K˜/K˜2K˜6K˜?Kšœœ˜—Kš œœœœœ˜3Kšœœœ,˜Jšœ ˜ Kšœ8˜8Kšœœœœ ˜-Kšœ˜Kšœ˜—K˜ K˜Kšœ.˜.K˜Kšœ%˜%K˜Kšœ*˜*K˜šœ+˜+Kšœœ!œœ˜O—šœ-œœ˜=Kšœœ$˜@K˜K˜Kšœœ˜—KšœœœG˜hK˜—K˜ K˜—K˜�š Ÿ œœ œœ9œ™všŸœ œœ™K™#šŸœœœœœœœœ œ™aKšœ œ™K™ Kšœ3™3Kšœœœœœœ™MK™Kšœœœ™%K™K™)K™*Kšœœ™K™—šŸ œœœ'œ™HK™K™—Kšœ+™+K™K™—K™ K™—K˜�šŸ œœ œœœ œ˜Hšœ˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—Kšœœœ˜1K˜—K˜�šŸœœ œœ˜6KšŸœ œœ˜9K˜K˜—K˜�šŸœ œœ˜7K˜K˜Kšœœ˜:K˜—K˜�Kšœœ%˜9K˜�šŸœ œœ˜7šŸœ œœ˜6Kšœœ˜Kšœ%˜%K˜—šŸœ œœ"˜Fšœ˜˜Kšœ"œ˜(K˜—˜ K˜Kšœ0˜0K˜—Kšœœ˜—K˜—Kšœ˜Kšœ4˜4Kšœ3˜3Kšœ˜K˜K˜—K˜�šŸœ œœ˜3šœ˜Kšœ˜Kšœ œ˜)Kšœœ˜—K˜—K˜�šŸ œœ œœ˜2KšŸœ œœ˜4K˜ K˜—K˜�šŸœ œœ˜3šŸœ œœ"˜@Kšœ-˜-K˜—Kšœœœ˜'K˜Kšœ.˜.Kšœ-˜-K˜K˜—K˜�šŸœœ œœ˜6KšŸœ œœ˜8K˜ K˜—K˜�šŸœ œœ˜7šœœœ˜*Kšœ˜šœ˜Kšœ˜Kšœ˜Kšœ-˜-Kšœ˜——K˜—K˜�š Ÿœœ œœ!œ˜gKšŸœ œœ+˜DK˜ K˜—K˜�šŸœ œœ!œ˜hKšœœœ˜Kšœ˜šœ&œœ˜9K˜Kšœ˜—Kšœ˜K˜—K˜�š Ÿœœ œœœ˜HKšŸœ œœ"˜;K˜ K˜—K˜�šŸœ œœœ˜IKšœœœ˜Kšœ˜šœ"œœ˜5K˜Kšœ˜—Kšœ˜K˜—K˜�šŸœœ œœ#œ'œ˜KšŸœ œœ)˜BK˜ K˜—K˜�šŸœœ œœ#œ'œ˜‡K˜šœ(œœ˜;Kšœ œœ˜K˜Kšœ˜—K˜K˜—K˜�š Ÿ œœ œœœ˜AKšŸœ œœ˜,K˜ K˜—K˜�š Ÿœœ œœœ˜IK˜K˜—K˜�šŸ œœœ˜$K˜K˜—K˜�šŸœœ œœ˜'šŸœ œœ˜šŸœ œœ˜,šœœœœ˜2K˜Kšœ œ˜Kšœœ˜K˜K˜ K˜K˜—K˜—Kšœœ1˜FKšœ,œ˜2Kšœ œ˜K˜K˜Kšœœ˜ K˜—K˜ K˜—K˜�š Ÿœœ œœœ˜BšŸœ œœ˜šŸœœœ˜$Kšœœ˜0Kšœœ˜,K˜—Kšœ(œ˜/K˜—K˜ K˜—K˜�šŸœœ œœ˜3šŸœ œœ˜K˜ Kš œœœœœ˜8šœœœœ˜2K˜Kšœœœœœœ˜-Kšœœœœœœ˜-Kšœœ˜Kšœ œ˜K˜K˜—Kšœ#˜#Kš œœ œ7œœ˜tKšœ˜K˜—K˜ K˜—K˜�šŸ œœœ˜;šœœ3œœ˜FK˜:Kš œœ3œœœ˜KK˜—K˜—K˜�šŸ œ œœ˜!Kšœœœ˜*Kšœ$˜$Kšœ#˜#Kšœœ˜Kšœœ˜šŸ œœ˜K˜$K˜Kšœœœœ*˜VK˜—šŸœ œœ˜)Kšœœœœ˜(šœœœ˜!Kšœ˜K˜K˜šœœ˜7K˜Kšœ˜—K˜Kšœ˜K˜—K˜Kšœœ˜šœ,œœ˜@K˜Kšœ˜—šœœœœ˜2K˜šœ0œœ˜DK˜Kšœ˜—K˜—K˜Kšœ$œœ˜1Kšœ˜K˜—Kšœœ˜K˜Oš˜šœ ˜ Kšœ˜#Kšœ œœ˜6—Kšœ˜Kšœ˜—KšœœE˜OK˜—K˜�šŸœœœŸœ˜^K˜šœœœ7œœ˜YKšœœœ˜Kšœ˜—K˜K˜—K˜�šŸœœ˜šœ˜ K˜/K˜-K˜GKšœ˜K˜GKšœ˜K˜—KšœD˜DK˜—K˜�K˜K˜�Kšœ˜—�…—����‡„��¿��