<> <> <> DIRECTORY Atom, BasicTime, CD, CDEvents USING [EventProc, RegisterEventProc], CDIO, CDOps USING [DoTheDelayedRedraws], CDSequencer, CDViewer, IO, MBQueue USING [Queue, Create, QueueClientAction], Process, RefTab, Rope, RuntimeError, TerminalIO USING [WriteRope, UserAbort], TokenIO, UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Number], ViewerOps; CDSequencerImpl: CEDAR MONITOR IMPORTS Atom, BasicTime, CD, CDEvents, CDIO, CDOps, CDViewer, IO, MBQueue, Process, RefTab, RuntimeError, TerminalIO, TokenIO, UserProfile, ViewerOps EXPORTS CDSequencer, CD = BEGIN Command: TYPE = CDSequencer.Command; CommandProc: TYPE = CDSequencer.CommandProc; QueueMethod: TYPE = CDSequencer.QueueMethod; CommandPrivate: TYPE = CDSequencer.CommandPrivate; priviledgedRefTab: RefTab.Ref = RefTab.Create[mod: 17]; -- holds commands not to be used globalRefTab: RefTab.Ref = RefTab.Create[mod: 53]; -- holds commands used in all technologies designNumber: INT_0; DesignEntry: TYPE = RECORD [ whileCommand: BOOL_FALSE, --modifyed exclusively by the monitored command procedures whileOutput: BOOL_FALSE, --modifyed exclusively by one single output process parity: BOOL_TRUE, abort: BOOL_FALSE, lastCommand: BasicTime.GMT, lastOutput: BasicTime.GMT, num: INT_0 ]; SequencerDesignPrivate: PUBLIC TYPE = DesignEntry; start: BasicTime.GMT = BasicTime.Now[]; now: BasicTime.GMT _ BasicTime.Now[]; TechnologyCommandsRep: PUBLIC TYPE = RECORD [ commandTab: RefTab.Ref ]; DesignHasBeenCreated: CDEvents.EventProc ={ design.queue _ MBQueue.Create[]; designNumber _ designNumber+1; design.seqPrivate _ NEW[DesignEntry_[lastCommand: now, lastOutput: now, num: designNumber]]; }; TechnologyHasBeenRegistered: CDEvents.EventProc = BEGIN tech: CD.Technology ~ NARROW[x]; tech.technologyCommands _ NEW[TechnologyCommandsRep _ [commandTab: RefTab.Create[mod: 31]]] END; InternalExecuteCommand: PROC [ref: REF ANY] = <<--caller must guarantee: comm.private#NIL>> BEGIN ENABLE { ABORTED => GOTO Aborted; TerminalIO.UserAbort => GOTO TerminalAborted; }; comm: Command = NARROW[ref]; de: REF DesignEntry = comm.design.seqPrivate; de.abort _ FALSE; IF comm.private.p#NIL THEN { IF comm.private.qm#dontQueue THEN { de.whileCommand _ TRUE; WHILE de.whileOutput DO TokenIO.StopWriting[]; IF de.whileOutput THEN Process.Pause[Process.MsecToTicks[1]] ENDLOOP; de.lastCommand _ now; IF comm.private.qm=doQueueAndMark THEN { comm.design.actual.first.changed _ TRUE; }; comm.private.p[comm]; de.whileCommand _ FALSE } ELSE comm.private.p[comm]; CDOps.DoTheDelayedRedraws[comm.design]; } ELSE { TerminalIO.WriteRope["command "]; TerminalIO.WriteRope[Atom.GetPName[comm.a]]; TerminalIO.WriteRope[" not loaded\n"]; }; EXITS Aborted => {TerminalIO.WriteRope["** ABORTED\n"]}; TerminalAborted => {TerminalIO.WriteRope["** user aborted\n"]}; END; ImplementCommand: PUBLIC ENTRY PROC [a: ATOM, p: CommandProc, technology: CD.Technology_NIL, queue: QueueMethod] = <<-- If technology is NIL implement for all technologies.>> <<-- There is a (finite, short) list of atoms you MUST NOT use.>> <<-- Reimplementing the same atom in a technology it already exists overwrites the command; >> <<-- If the same atom is specified for a particular technology and all technologies,>> <<-- the more specific implementation wins independent of order. >> <<-- Don't fool with NIL CommandProc's if you don't know EXACTLY what it does.>> <<-- (NIL CommandProc's serves too install the list of commands you MUST NOT use.)>> BEGIN ENABLE UNWIND => NULL; ptr: REF CommandPrivate ~ NEW[CommandPrivate_[p: p, qm: queue]]; myRefTab: RefTab.Ref _ globalRefTab; <<-- check if is reserved atom not to be used>> IF p=NIL THEN {[] _ RefTab.Insert[priviledgedRefTab, a, technology]; RETURN} ELSE IF RefTab.Fetch[priviledgedRefTab, a].found THEN RETURN WITH ERROR CD.Error[doubleRegistration, "This atom is reserved"]; <<-- do implementation>> IF technology#NIL THEN { techCommands: REF TechnologyCommandsRep ~ technology.technologyCommands; myRefTab _ techCommands.commandTab; }; [] _ RefTab.Store[myRefTab, a, ptr] END; <> <<-- oldy, for speed reason don't call ExecuteCommand>> <> <> <> <> <> <> ExecuteCommand: PUBLIC PROC [comm: Command, design: CD.Design, as: ATOM, queue: CDSequencer.QueueMethod] = <<-- if as#NIL replaces comm.a>> <<-- if design#NIL replaces comm.design >> BEGIN BuildCommandPrivate: PROC[] = INLINE <<--uses globals newCom and queue>> BEGIN found: BOOLEAN _ FALSE; val: RefTab.Val; IF newComm.design.technology#NIL THEN { -- search first for particular technology cTab: REF TechnologyCommandsRep = newComm.design.technology.technologyCommands; [found, val] _ RefTab.Fetch[cTab.commandTab, newComm.a]; }; IF NOT found THEN [found, val] _ RefTab.Fetch[globalRefTab, newComm.a]; -- search global IF found THEN newComm.private _ NARROW[val, REF CommandPrivate] ELSE newComm.private _ NEW[CommandPrivate _ [p: NIL, qm: queue]] --usually <<--we queue the message for unknown command; take it from queue>> END; newComm: CDSequencer.Command ~ NEW[CDSequencer.CommandRec_comm^]; IF as#NIL THEN newComm.a _ as; IF design#design THEN newComm.design _ design; IF newComm.design=NIL THEN ERROR; BuildCommandPrivate[]; IF queue#useDefault AND newComm.private.qm#queue THEN newComm.private _ NEW[CommandPrivate _ [p: newComm.private.p, qm: queue]]; IF newComm.private.qm#dontQueue THEN MBQueue.QueueClientAction[newComm.design.queue, InternalExecuteCommand, newComm] ELSE InternalExecuteCommand[newComm] END; AbortTheCommand: PUBLIC PROC [design: CD.Design] = BEGIN de: REF DesignEntry = design.seqPrivate; IF de.whileCommand THEN de.abort _ TRUE END; Aborted: PUBLIC PROC [design: CD.Design] RETURNS [BOOL] = BEGIN de: REF DesignEntry = design.seqPrivate; RETURN [de.abort] END; CheckAborted: PUBLIC PROC [design: CD.Design] = <<--IF Aborted[design] THEN SIGNAL TerminalIO.UserAbort>> BEGIN de: REF DesignEntry = design.seqPrivate; IF de.abort THEN SIGNAL TerminalIO.UserAbort END; <<>> SaveOneDesign: PROC [design: CD.Design] = BEGIN ENABLE { TokenIO.WritingStopped => GOTO end; RuntimeError.UNCAUGHT => GOTO end; }; de: REF DesignEntry = design.seqPrivate; saveOn: Rope.ROPE; done: BOOL; IF de.parity THEN saveOn _ IO.PutFR["///temp/chipndale/saved%01gA.dale", IO.int[de.num]] ELSE saveOn _ IO.PutFR["///temp/chipndale/saved%01gB.dale", IO.int[de.num]]; done _ CDIO.WriteDesign[design: design, to: saveOn, quiet: TRUE, emergency: FALSE]; IF done THEN { de.lastOutput _ BasicTime.Now[]; de.parity _ ~de.parity; <<-- make garbage collection possible>> <> }; TokenIO.ReleaseWriter[]; EXITS end => NULL END; EmergencySaveProcess: PROC = BEGIN SaveAViewer: ViewerOps.EnumProc -- PROC [v: Viewer] RETURNS [BOOL _ TRUE] -- = BEGIN design: CD.Design = CDViewer.DesignOf[v].design; -- is not monitored IF design#NIL THEN { de: REF DesignEntry = design.seqPrivate; IF de#NIL AND NOT de.whileCommand AND BasicTime.Period[from: de.lastCommand, to: now]>15 AND BasicTime.Period[from: de.lastOutput, to: de.lastCommand]>savePeriod THEN { de.whileOutput _ TRUE; <<--Hint for correctness proof:>> <<--this (above) assignment statement to de.whileOutput happens either before >> <<--or after an assgnment to de.whileCommand or there is no conflict at all>> <<--If this before: in command process we wait until outout is finished>> <<--If this after: we skip the output (next if statement) >> IF NOT de.whileCommand THEN SaveOneDesign[design]; }; IF de#NIL THEN de.whileOutput _ FALSE } END; DO -- forever now _ BasicTime.Now[]; Process.Pause[Process.SecondsToTicks[10]]; IF savePeriod>=0 THEN ViewerOps.EnumerateViewers[SaveAViewer] ENDLOOP; END; savePeriod: INT _ 0; NoteProfileChange: UserProfile.ProfileChangedProc -- PROC [reason: ProfileChangeReason]-- = BEGIN savePeriod _ UserProfile.Number[key: "Chipndale.SavePeriod", default: 0]; END; CDEvents.RegisterEventProc[$RegisterTechnology, TechnologyHasBeenRegistered]; CDEvents.RegisterEventProc[$CreateNewDesign, DesignHasBeenCreated]; UserProfile.CallWhenProfileChanges[NoteProfileChange]; TRUSTED {Process.Detach[FORK EmergencySaveProcess[]]}; END.