DIRECTORY DecomposerControl USING [FreeDecomposeObject], NSFile USING [nullID], NSString USING [String, StringFromMesaString], PrintingTypes USING [Option], PrintQueue USING [ Empty, nilQueueObjectHandle, QueueObjectHandle, Requeue, QueueStage, QueueWatcher], Process USING [ Detach, Priority, priorityBackground, SecondsToTicks, SetPriority, SetTimeout], PSAsyncMsg USING [ Expand1AndPutString, ExpandArrayAndPutString, Proc, PutMesaString], QueueControl USING [CompletionProc, TraceLevel], QueueFile USING [Delete], String USING [AppendLongDecimal], System USING [GetGreenwichMeanTime, SecondsSinceEpoch], Time USING [Append, Unpack]; QueueControlImpl: CEDAR MONITOR IMPORTS DecomposerControl, NSString, PrintQueue, Process, PSAsyncMsg, QueueFile, String, System, Time EXPORTS QueueControl = BEGIN State: TYPE = RECORD [ trace: QueueControl.TraceLevel _ none, docCompleted: QueueControl.CompletionProc _ NIL ]; state: State; abortedQEvent: CONDITION; -- something on aborted queue markedQEvent: CONDITION; -- something on marked queue forwardedQEvent: CONDITION; -- something on forwarded queue abortQProcessed: CONDITION; -- something moved from aborted to inactive queue markedQProcessed: CONDITION; -- something moved from marked to inactive queue forwardedQProcessed: CONDITION; -- something moved from forwarded to inactive queue Init: PUBLIC ENTRY PROCEDURE [printingOption: PrintingTypes.Option, documentCompleted: QueueControl.CompletionProc, putAsyncMsgFromKey: PSAsyncMsg.Proc, trace: QueueControl.TraceLevel] = BEGIN state.trace _ trace; state.docCompleted _ documentCompleted; Process.Detach[FORK ProcessAbortedQueue[]]; Process.SetTimeout[@abortedQEvent, Process.SecondsToTicks[seconds: 180]]; IF printingOption = feps9700 THEN BEGIN Process.Detach[FORK ProcessForwardedQueue[]]; Process.SetTimeout[@forwardedQEvent, Process.SecondsToTicks[seconds: 60]]; END ELSE BEGIN Process.Detach[FORK ProcessMarkedQueue[]]; Process.SetTimeout[@markedQEvent, Process.SecondsToTicks[seconds: 60]]; END; UNTIL PrintQueue.Empty[aborted] DO WAIT abortQProcessed; ENDLOOP; IF printingOption = feps9700 THEN UNTIL PrintQueue.Empty[forwarded] DO WAIT forwardedQProcessed; ENDLOOP ELSE UNTIL PrintQueue.Empty[marked] DO WAIT markedQProcessed; ENDLOOP; END; --Init ModifyTraceLevel: PUBLIC ENTRY PROCEDURE [trace: QueueControl.TraceLevel] = BEGIN state.trace _ trace; END; --ModifyTraceLevel WaitAbortQueueEmpty: PUBLIC ENTRY PROCEDURE = BEGIN UNTIL PrintQueue.Empty[aborted] DO WAIT abortQProcessed; ENDLOOP; END; -- WaitAbortQueueEmpty AbortedQEntry: ENTRY PROCEDURE = BEGIN -- Was nested in ProcessAbortedQueue called by PrintQueue when a QObj shows up on aborted queue ENABLE UNWIND => NULL; BROADCAST abortedQEvent; IF state.trace = verbose THEN PSAsyncMsg.PutMesaString["$$BROADCAST abortedQEvent"]; END; -- AbortedQEntry ProcessAbortedQueue: PUBLIC ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; jobCount: CARDINAL _ 0; abortedQInitialized: BOOLEAN _ FALSE; qOH: PrintQueue.QueueObjectHandle _ PrintQueue.nilQueueObjectHandle; Process.SetPriority[Process.priorityBackground]; PrintQueue.QueueWatcher[AbortedQEntry, aborted]; DO -- forever IF abortedQInitialized AND (jobCount = 0) THEN WAIT abortedQEvent; IF jobCount < 10 THEN jobCount _ jobCount + 1 ELSE jobCount _ 0; qOH _ PrintQueue.Requeue[fromQueue: aborted, toQueue: temp]; IF qOH = PrintQueue.nilQueueObjectHandle THEN {abortedQInitialized _ TRUE; jobCount _ 0; LOOP}; IF qOH.printObjectHandle # NIL THEN BEGIN DecomposerControl.FreeDecomposeObject[qOH.uid]; qOH.printObjectHandle _ NIL; END; IF NOT qOH.bannerOnly AND (qOH.currentStatus IN [sysRestartInSpooler..sysRestartInQueue] OR qOH.currentStatus IN [decomposeFailure..markFailure]) THEN BEGIN WITH q: qOH SELECT FROM feps9700 => IF q.ivFileID # NSFile.nullID THEN q.ivFileID _ QueueFile.Delete[q.ivFileID]; ENDCASE; qOH.priorStatus _ qOH.currentStatus; qOH.bannerOnly _ TRUE; qOH.completionDate _ System.GetGreenwichMeanTime[]; IF state.trace # none THEN BEGIN PSAsyncMsg.Expand1AndPutString["$$Document \"<1>\" Moved to BannerOnlyQ", qOH.fileName]; END; [] _ PrintQueue.Requeue[qOH: qOH, fromQueue: temp, toQueue: bannerOnly]; END ELSE BEGIN IF qOH.bannerOnly THEN qOH.currentStatus _ qOH.priorStatus; qOH.completionDate _ BasicTime.Now[]; SELECT qOH.currentStatus FROM spoolFailure => state.docCompleted[status: spoolFailure, documentID: qOH.uid]; ENDCASE => WITH q: qOH SELECT FROM fax495 => BEGIN printed: CARDINAL _ IF q.localPrintStatus = printed THEN 1 ELSE 0; transmitted: CARDINAL _ 0; FOR i: CARDINAL IN [0..q.phoneNoCount) DO IF q.transmitData[i].status = transmitted THEN transmitted _ transmitted + 1 ENDLOOP; state.docCompleted[status: aborted, documentID: q.uid, docsPrinted: printed, docsTransmitted: transmitted]; END; ENDCASE => state.docCompleted[status: aborted, documentID: q.uid]; IF state.trace # none THEN BEGIN nsAborted: NSString.String = Str["$$Document ""<1>"" Aborted"L]; PSAsyncMsg.Expand1AndPutString[nsAborted, qOH.fileName]; END; [] _ PrintQueue.Requeue[ qOH: qOH, fromQueue: temp, toQueue: inactive]; END; BROADCAST abortQProcessed; ENDLOOP; END; -- ProcessAbortedQueue MarkedQEntry: ENTRY PROCEDURE = -- called by PrintQueue when a QObj shows up on marked queue BEGIN ENABLE UNWIND => NULL; BROADCAST markedQEvent; IF state.trace = verbose THEN PSAsyncMsg.PutMesaString["$$BROADCAST markedQEvent"L]; END; -- MarkedQEntry ProcessMarkedQueue: PUBLIC ENTRY PROCEDURE = BEGIN markedQInitialized: BOOLEAN _ FALSE; qOH: PrintQueue.QueueObjectHandle _ PrintQueue.nilQueueObjectHandle; Process.SetPriority[Process.priorityBackground]; PrintQueue.QueueWatcher[MarkedQEntry, marked]; DO -- forever IF markedQInitialized THEN WAIT markedQEvent; <> qOH _ PrintQueue.Requeue[fromQueue: marked, toQueue: temp]; IF qOH = PrintQueue.nilQueueObjectHandle THEN {markedQInitialized _ TRUE; LOOP}; IF qOH.printObjectHandle # NIL THEN BEGIN DecomposerControl.FreeDecomposeObject[qOH.uid]; qOH.printObjectHandle _ NIL; END; IF qOH.bannerOnly THEN qOH.currentStatus _ qOH.priorStatus; qOH.completionDate _ System.GetGreenwichMeanTime[]; WITH q: qOH SELECT FROM fax495 => BEGIN printed: CARDINAL _ IF q.localPrintStatus = printed THEN 1 ELSE 0; transmitted: CARDINAL _ 0; FOR i: CARDINAL IN [0..q.phoneNoCount) DO IF q.transmitData[i].status = transmitted THEN transmitted _ transmitted + 1; ENDLOOP; IF qOH.bannerOnly THEN state.docCompleted[status: aborted, documentID: q.uid, docsPrinted: printed, docsTransmitted: transmitted] ELSE state.docCompleted[status: successful, documentID: q.uid, docsPrinted: printed, docsTransmitted: transmitted]; END; ENDCASE => IF qOH.bannerOnly THEN state.docCompleted[ status: aborted, documentID: q.uid, docsPrinted: 0] ELSE state.docCompleted[ status: successful, documentID: q.uid, docsPrinted: 1]; IF state.trace # none THEN BEGIN nsCompleted: NSString.String = Str["$$Document ""<1>"" Completed: <2>; Elapsed time since decomposing: <3>"L]; stringArray: ARRAY [0..3) OF NSString.String; startSinceEpoch: LONG CARDINAL _ System.SecondsSinceEpoch[qOH.startDecomposeDate]; endSinceEpoch: LONG CARDINAL _ System.SecondsSinceEpoch[qOH.completionDate]; timeElapsed: LONG CARDINAL _ 0; sTime: STRING _ [20]; sElapsed: STRING _ [20]; stringArray[0] _ qOH.fileName; Time.Append[sTime, Time.Unpack[qOH.completionDate]]; stringArray[1] _ Str[sTime]; IF endSinceEpoch >= startSinceEpoch THEN timeElapsed _ endSinceEpoch - startSinceEpoch; String.AppendLongDecimal[sElapsed, timeElapsed]; stringArray[2] _ Str[sElapsed]; PSAsyncMsg.ExpandArrayAndPutString[nsCompleted, DESCRIPTOR[stringArray]]; END; [] _ PrintQueue.Requeue[ qOH: qOH, fromQueue: temp, toQueue: inactive]; BROADCAST markedQProcessed; ENDLOOP; END; -- ProcessMarkedQueue ProcessForwardedQueue: PUBLIC ENTRY PROCEDURE = BEGIN <> ForwardedQEntry: ENTRY PROCEDURE = -- called by PrintQueue when a QObj shows up on forwarded queue BEGIN ENABLE UNWIND => NULL; BROADCAST forwardedQEvent; IF state.trace = verbose THEN PSAsyncMsg.PutMesaString["$$BROADCAST forwardedQEvent"L]; END; -- ForwardedQEntry forwardedQInitialized: BOOLEAN _ FALSE; qOH: PrintQueue.QueueObjectHandle _ PrintQueue.nilQueueObjectHandle; Process.SetPriority[Process.priorityBackground]; PrintQueue.QueueWatcher[ForwardedQEntry, forwarded]; DO -- forever IF forwardedQInitialized THEN WAIT forwardedQEvent; <> qOH _ PrintQueue.Requeue[fromQueue: forwarded, toQueue: temp]; IF qOH = PrintQueue.nilQueueObjectHandle THEN {forwardedQInitialized _ TRUE; LOOP}; IF qOH.bannerOnly THEN qOH.currentStatus _ qOH.priorStatus; qOH.completionDate _ System.GetGreenwichMeanTime[]; WITH q: qOH SELECT FROM feps9700 => IF qOH.bannerOnly THEN state.docCompleted[ status: aborted, documentID: q.uid, docsPrinted: 0] ELSE state.docCompleted[ status: successful, documentID: q.uid, docsPrinted: 1]; ENDCASE => ERROR; --only feps9700 uses the forwarded queue IF state.trace # none THEN BEGIN nsCompleted: NSString.String = Str["$$Document ""<1>"" Completed: <2>; Elapsed time since decomposing: <3>"L]; stringArray: ARRAY [0..3) OF NSString.String; startSinceEpoch: LONG CARDINAL _ System.SecondsSinceEpoch[ qOH.startDecomposeDate]; endSinceEpoch: LONG CARDINAL _ System.SecondsSinceEpoch[qOH.completionDate]; timeElapsed: LONG CARDINAL _ 0; sTime: STRING _ [20]; sElapsed: STRING _ [20]; stringArray[0] _ qOH.fileName; Time.Append[sTime, Time.Unpack[qOH.completionDate]]; stringArray[1] _ Str[sTime]; IF endSinceEpoch >= startSinceEpoch THEN timeElapsed _ endSinceEpoch - startSinceEpoch; String.AppendLongDecimal[sElapsed, timeElapsed]; stringArray[2] _ Str[sElapsed]; PSAsyncMsg.ExpandArrayAndPutString[nsCompleted, DESCRIPTOR[stringArray]]; END; [] _ PrintQueue.Requeue[ qOH: qOH, fromQueue: temp, toQueue: inactive]; BROADCAST forwardedQProcessed; ENDLOOP; END; -- ProcessForwardedQueue Str: PROCEDURE [s: REF TEXT] RETURNS [ns: NSString.String] = INLINE { RETURN[NSString.StringFromMesaString[s]]}; END. -- of QueueControlImpl LOG ****EARLIER LOG ENTRIES DELETED. See archived version from 8.0. 17-Sep-84 16:18:39 - Jacks - Added ModifyTraceLevel and ProcessForwardedQueue. 16-Oct-84 11:27:44 - Jacks - Added actual code to ProcessForwardedQueue. 15-Nov-84 17:16:20 - Jacks - ProcessAbortedQueue now deleted interleaved interpress master file, if any, before placing document on bannerOnly queue; updated to new QueueControl interface. 17-Jun-85 15:46:28 - Jacks - Added copyright notice. 17-Jul-85 14:26:31 - Jacks - New PSAsyncMsg interface. 6-Aug-85 9:15:57 - Jacks - Updated to 10.0 DecomposerControl.FreeDecomposeObject. 15-Nov-85 10:23:30 - Jacks - Count bannerOnly docs as aborted instead of printed. 11-Apr-86 11:22:43 - Jacks - MAJOR CHANGES to prevent bug which occured when many jobs were aborted because of spool failure. PrintQueueImpl was forking processes to call AbortedQEntry right and left, but they were all monitor locked out. This was because ProcessAbortedQueue just kept processing the aborted queue because the queue was never empty. It never gave up the lock and eventually the server crashed when over 140 processes had been forked by PrintQueueImpl (error = TooManyProcesses). To prevent this, ProcessAbortedQueue now processes only ten jobs at a time, then it must wait to be notified by PrintQueueImpl. Hopefully waiting after every ten jobs will prevent PrintQueueImpl from forking too many processes than never get through. I tried processing only one job at a time, but that didn't work because sometimes AbortedQEntry would be executed more than once before ProcessAbortedQueue would run, causing ProcessAbortedQueue to miss some notifies and leave jobs orphaned on the aborted queue. ProcessMarkedQueue and ProcessForwardedQueue don't have the problem of jobs coming on their queues to quickly to keep up. So those procedures process one job every time they are notified by PrintQueueImpl. Also, since they are very often used, there are timeouts on the conditions variables which wake them up (just in case a notify is missed or something). 21-Apr-86 14:12:16 - Jacks - Modified trace code. 16-May-86 15:02:03 - Jacks - No longer check QEntry variables when waiting on QEvents; when event is broadcast, processing begins and we check for nilQueueObjectHandle. *QueueControlImpl.mesa Copyright (C) Xerox Corporation 1984, 1985, 1986. All rights reserved. Last edited by Jacks: 16-May-86 15:04:17 Tim Diebert: December 23, 1986 1:24:22 pm PST <> start queue watcher processes... all jobs found on these queues must be processed before initialization is completed... -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- <> <> Delete the interleaved interpress master, if any, before putting doc on bannerOnly queue: Notify command level that a document was aborted and send pertenant data... A fax job may be aborted AFTER a local print and some number of remote transmissions. Requeue deletes spooled file and interleaved interpress master, if any... -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- <> Notify command level that a document has completed and send pertenant data... Name of document Get current time Get elapsed time Requeue deletes spooled file... -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- -- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- Notify command level that a document was forwarded and send pertenant data... Get document name Get current time Get elapsed time Requeue deletes interleaved interpress master... (Spooled file was deleted by MergeControlImpl) Κ ‚˜codešœ™KšœF™FKšœ(™(K™-—K˜K™UK˜šΟk ˜ Kšœœ˜.Kšœœ ˜Kšœ œ ˜.Kšœœ ˜šœ œ˜K˜DK˜—šœœ˜K˜O—šœ œ˜K˜C—Kšœ œ˜0Kšœ œ ˜Kšœœ˜!Kšœœ+˜7Kšœœ˜—K˜šΠlnœœ˜š˜K˜]—Kšœ˜Kš˜K˜šœœœ˜K˜&Kšœ,˜/K˜—K˜ —˜Kšœ œΟc˜8Kšœ œŸ˜6Kšœ œŸ˜;Kšœ œŸ1˜NKšœ œŸ0˜NKšœ œŸ3˜TK˜š Οnœœœ œŸ˜ΐK˜K˜'Kšœ ™ Kšœœ˜+K˜Išœ˜šœ˜ Kšœœ˜-K˜JKš˜—šœ˜ Kšœœ˜*K˜GKšœ˜——Kšœ8™8Kšœ™Kšœœœœ˜Ašœ˜Kš œœœ˜KKš œœœœ˜F—KšœŸ˜ K˜—K˜š  œœœ œ#˜QK˜KšœŸ˜K˜——˜KšœI™IKš  œœœ œ˜3šœI™IKšœœœœ˜AKšœŸ˜K˜K˜—KšœI™IKš   œœ œœŸ$˜KšœI™IKšŸ:˜:Kšœœœ˜Kš œ˜šœ˜K˜6—KšœŸ˜K˜—KšœI™IKš  œœœ œ˜3šœI™IKšœ ŸœΗ™υKšœœœ˜Kšœ œ˜Kšœœœ˜&K˜DK˜0K˜0šœŸ ˜šœœœœ˜BK™ς—Kšœœœ˜@K˜<šœ'˜-Kšœœœ˜1—šœœœ˜)K˜/Kšœœ˜Kšœ˜—šœœ˜Kšœœ)œœ!˜{šœ˜ KšœY™Yšœœ˜šœ œ˜.Kšœ*˜*—Kšœ˜—K˜$Kšœœ˜K˜3šœœ˜ K˜XKšœ˜—K˜HKš˜—šœ˜ Kšœœ%˜;K˜%K˜KšœK™Kšœ˜K˜Nšœœœ˜"šœ ˜KšœU™Ušœ œ˜Kšœœœ˜.—Kšœ œ˜šœœœ˜)Kšœ(œ˜LKšœ˜—K˜kKšœ˜—Kšœ;˜B—K˜—šœœ˜ K˜@K˜8Kšœ˜—KšœI™I˜K˜.—Kšœ˜——Kš œ˜Kšœ˜—KšœŸ˜K˜K˜—š  œœ œŸ<˜]Kš˜Kšœœœ˜Kš œ˜šœ˜K˜6—KšœŸ˜K˜—KšœI™IKš  œœœ œ˜2šœI™IK™‰K˜Kšœœœ˜$K˜DK˜0K˜.šœŸ ˜šœœœ˜-K˜EK˜AK˜AK˜'—K˜;Kšœ'œœœ˜Pšœœœ˜)K˜/Kšœœ˜Kšœ˜—Kšœœ%˜;K˜3K˜Kšœ2™2Kšœ™šœœ˜šœ ˜šœ œ˜Kšœœœ˜.—Kšœ œ˜šœœœ˜)šœ(œ˜/K˜—Kšœ˜—šœ˜˜7K˜3——šœ;˜?K˜4—Kšœ˜—šœœ˜!˜K˜3—šœ˜K˜7——K˜—šœœ˜ K˜nKšœ œœ˜-šœœ˜K˜3—šœœ˜K˜/—Kšœ œœ˜Kšœœ˜Kšœ œ˜Kšœ™K˜Kšœ™K˜4K˜Kšœ™šœ"˜(K˜.—K˜0K˜Kšœ0 œ˜IKšœ˜K˜—Kšœ™˜K˜.—Kš œ˜Kšœ˜—KšœŸ˜K˜K˜—KšœI™Iš œœœ œ˜/KšœI™IKš˜K˜KK˜CK˜AK˜:K˜Kšœ'œœœ˜SKšœœ%˜;K˜3K˜Kšœ2™2Kšœ™šœœ˜šœ œ˜"˜K˜3—šœ˜K˜7——KšœœŸ(˜:K˜—šœœ˜ K˜nKšœ œœ˜-šœœ˜K˜4—šœœ˜K˜/—Kšœ œœ˜Kšœœ˜Kšœ œ˜Kšœ™K˜Kšœ™K˜4K˜Kšœ™šœ"˜(K˜.—K˜0K˜Kšœ0 œ˜IKšœ˜K˜—Kšœ0™0Kšœ.™.˜K˜.K˜—Kš œ˜Kšœ˜—KšœŸ˜K˜—š  œ œœœœœ˜EKšœ$˜*——˜KšœŸ˜—K˜Kš˜Kš œœœœœ"˜AK˜NK˜HK˜ΌK˜4˜6K˜R—K˜QšœœœΕ˜οK˜K˜ε—K˜1K˜¨—…—3>Kκ