<> <> <> <> DIRECTORY CedarProcess, CD USING [Design], CDIO USING [WriteDesign], CDDraw, CDVPrivate, CDViewer USING [DesignOf], CDSequencer, IO USING [PutFR, int], MBQueue USING [Flush], Process USING [Detach, MsecToTicks, Pause, SecondsToTicks], Rope USING [ROPE, Cat, IsEmpty], RuntimeError USING [UNCAUGHT], Terminal USING [GetKeys, Virtual, Current], TerminalIO USING [WriteRope], ViewerClasses USING [ViewerClass, SaveProc], ViewerOps USING [FetchViewerClass, EnumerateViewers, EnumProc]; CDEmergencyHandling: CEDAR MONITOR IMPORTS CedarProcess, CDIO, CDDraw, CDVPrivate, CDViewer, CDSequencer, IO, MBQueue, Process, Rope, RuntimeError, Terminal, TerminalIO, ViewerOps SHARES CDVPrivate = BEGIN SaveAll: PROC [] = BEGIN CallTheSaver: ViewerOps.EnumProc -- PROC [v: Viewer] RETURNS [BOOL _ TRUE] -- = BEGIN design: CD.Design = CDViewer.DesignOf[v].design; -- is not monitored IF design#NIL THEN Save[v, TRUE] END; <<>> <<--I checked the implementation; >> <<--ViewerOps.EnumerateViewers then was really not monitored>> ViewerOps.EnumerateViewers[CallTheSaver] END; StopDrawing: PROC[] = BEGIN ENABLE RuntimeError.UNCAUGHT => GOTO exit; FOR l: CDVPrivate.MyGraphicRef _ CDVPrivate.linkBase, l.link WHILE l#NIL DO IF l.ct#NIL THEN CDDraw.FlushCommands[l.ct] ENDLOOP; EXITS exit => NULL; END; StopDrawingCommand: PROC [comm: CDSequencer.Command] = BEGIN TerminalIO.WriteRope["Stop drawing (better use )\n"]; StopDrawing[]; END; EmergencySaveProcess: PROC = BEGIN virtual: Terminal.Virtual _ Terminal.Current[]; CedarProcess.SetPriority[CedarProcess.Priority[foreground]]; DO -- for nearly ever Process.Pause[Process.SecondsToTicks[1]]; IF Terminal.GetKeys[virtual][ESC]=down AND Terminal.GetKeys[virtual][LF]=down THEN StopDrawing[]; IF Terminal.GetKeys[virtual][Spare3]=down AND Terminal.GetKeys[virtual][RightShift]=down AND Terminal.GetKeys[virtual][LeftShift]=down THEN { SaveAll[]; TerminalIO.WriteRope["\nyou got it; this command will not work a second time\n"]; RETURN } ENDLOOP; END; <<-- -- -- -- -- -- -- -- -- -- -- >> <<-- now real stuff>> <<>> Save: ViewerClasses.SaveProc --PROC [self: Viewer, force: BOOL _ FALSE]-- = BEGIN IF force AND ~self.saveInProgress THEN { <<--otherwise don't consider saving>> design: CD.Design = CDViewer.DesignOf[self].design; -- is not monitored IF design#NIL THEN { <<--vanilla try to avoid concurrency with the write, which is unmonitored;>> <<--forked, so save may not hang in MBQueue monitor locking on the design>> TRUSTED {Process.Detach[FORK MBQueue.Flush[design.queue]]}; <<--forked, so save of other designs may not hang>> TRUSTED {Process.Detach[FORK SaveDesign[design]]}; } } END; SaveDesign: PROC[design: CD.Design] = BEGIN done: BOOL _ FALSE; saveOn: Rope.ROPE; --using our own monitor prevents it at least from wedging together with other's; <<--we must use some monitor because emergencywrite tries to kill any interferences,>> <<--which could be other emergencywrite's >> ReallySaveDesign: ENTRY PROC[design: CD.Design] = BEGIN ENABLE BEGIN UNWIND => NULL; RuntimeError.UNCAUGHT => GOTO next; END; <<>> <<--we use the list business to prevent multiple save instead of CDValue, >> <<--to never call any monitored procedure which could cause additional wedges>> FOR list: LIST OF CD.Design _ savedDesigns, list.rest WHILE list#NIL DO IF list.first=design THEN RETURN; ENDLOOP; saveOn _ IO.PutFR["///temp/ChipNDale/emergencysave%01g.dale", IO.int[saveKey^]]; saveKey^ _ saveKey^+1; done _ CDIO.WriteDesign[design: design, to: saveOn, emergency: TRUE]; IF done THEN savedDesigns _ CONS[design, savedDesigns]; EXITS next => NULL END; Process.Pause[Process.MsecToTicks[100]]; -- give the detached Flush process a chance ReallySaveDesign[design]; IF done THEN TerminalIO.WriteRope[Rope.Cat["design ", (IF Rope.IsEmpty[design.name] THEN "no name" ELSE design.name), " emergency saved on file """, saveOn, """\nWARNING: This write was made out of synchronisation; (what means: if any other process was fooling with the design, both could be broken, the design, and the file). Roll back, Copy the file and CHECK it.\n"]]; END; savedDesigns: LIST OF CD.Design _ NIL; saveKey: REF INT = NEW[INT_0]; -- ref used as key; referent used as counter class: ViewerClasses.ViewerClass = ViewerOps.FetchViewerClass[$ChipNDale]; IF class#NIL AND class.save=NIL THEN class.save _ Save; TRUSTED {Process.Detach[FORK EmergencySaveProcess[]]}; CDSequencer.ImplementCommand[$StopDrawing, StopDrawingCommand,, dontQueue]; END.