<> <> <> <> <<<>>> 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; <> <<(Spooled file was deleted by MergeControlImpl)>> [] _ 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.