IPPrinterQueueImpl.mesa
Copyright (C) 1984, 1985, Xerox Corporation. All rights reserved.
Michael Plass, April 10, 1985 5:28:48 pm PST
Tim Diebert: July 25, 1986 9:16:20 am PDT
Rick Beach, December 13, 1985 12:55:29 pm PST
DIRECTORY
BasicTime,
FS,
IO,
Convert, RefText,
IPPrinterQueue,
Rope;
IPPrinterQueueImpl: MONITOR
IMPORTS BasicTime, FS, IO, Rope, Convert, RefText
EXPORTS IPPrinterQueue
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
Request: TYPE = IPPrinterQueue.Request;
showTimes: BOOLTRUE;
diskFileName: ROPENIL;
deferSaving: BOOLEANFALSE;
saveDeferred: BOOLEANFALSE;
RequestStatus: TYPE ~ IPPrinterQueue.RequestStatus; -- {notFound, canceled, waiting, printing}
RequestQueue: TYPE ~ REF RequestQueueRep;
RequestQueueRep: TYPE ~ RECORD [
next: RequestQueue ← NIL,
number: CARDINAL ← 0,
request: IPPrinterQueue.Request ← [NIL, NIL, NIL, NIL, NIL, 0],
status: RequestStatus ← notFound];
queueHead: RequestQueue ← NIL;
reqNumber: CARDINAL ← 0;
Save: PROC = {
IF deferSaving THEN {saveDeferred ← TRUE; RETURN};
IF diskFileName = NIL THEN RETURN;
DumpToDisk[diskFileName, queueHead];
saveDeferred ← FALSE;
};
DumpToDisk: PROC [diskFileName: ROPE, queueHead: RequestQueue] = BEGIN
s: IO.STREAMFS.StreamOpen[diskFileName, create ! FS.Error => GOTO Out];
DO
ENABLE FS.Error => EXIT;
IF queueHead = NIL THEN EXIT;
IO.PutF1[s, "%g ", IO.int[queueHead.number]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.fileName]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.requestTime]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.requestor]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.requestorPassword]];
IO.PutF1[s, "\"%g\" ", IO.rope[queueHead.request.separator]];
IO.PutF1[s, "%g ", IO.card[queueHead.request.copies]];
SELECT queueHead.status FROM
notFound => IO.PutRope[s, "notFound\n"];
canceled => IO.PutRope[s, "canceled\n"];
waiting => IO.PutRope[s, "waiting\n"];
printing => IO.PutRope[s, "printing\n"];
ENDCASE => ERROR;
queueHead ← queueHead.next;
ENDLOOP;
IO.Close[s ! FS.Error => GOTO Out];
EXITS Out => RETURN;
END;
Restore: PROC = {
IF diskFileName = NIL THEN RETURN;
queueHead ← LoadFromDisk[diskFileName];
};
LoadFromDisk: PROC [diskFileName: ROPE] RETURNS [queueHead: RequestQueue ← NIL] = BEGIN
s: IO.STREAMFS.StreamOpen[diskFileName, create ! FS.Error => GOTO Out];
r: CARDINAL; rope: ROPE;
tail: RequestQueue ← NIL;
DO
ENABLE FS.Error => EXIT;
r ← IO.GetCard[s ! IO.EndOfStream => EXIT];
IF tail = NIL THEN queueHead ← tail ← NEW[RequestQueueRep]
ELSE {tail.next ← NEW[RequestQueueRep]; tail ← tail.next};
tail.number ← r;
tail.request.fileName ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.requestTime ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.requestor ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.requestorPassword ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.separator ← IO.GetRopeLiteral[s ! IO.EndOfStream => GOTO Out];
tail.request.copies ← IO.GetCard[s ! IO.EndOfStream => GOTO Out];
rope ← IO.GetTokenRope[s ! IO.EndOfStream => GOTO Out].token;
tail.status ← SELECT TRUE FROM
Rope.Equal[rope, "notFound", FALSE] => notFound,
Rope.Equal[rope, "canceled", FALSE] => canceled,
Rope.Equal[rope, "waiting", FALSE] => waiting,
Rope.Equal[rope, "printing", FALSE] => printing,
ENDCASE => ERROR;
ENDLOOP;
reqNumber ← r;
IO.Close[s ! FS.Error => GOTO Out];
EXITS Out => RETURN [NIL];
END;
InternalReset: INTERNAL PROC = {
queueHead ← NIL;
};
Reset: PUBLIC ENTRY PROC = {InternalReset[]; Save[]};
newRequest: CONDITION;
requestLimit: CARDINAL ← 1000;
Requests are numbered modulo this number.
QueueRequest: PUBLIC ENTRY PROC [request: Request] RETURNS [requestNumber: INT] = {
ENABLE UNWIND => NULL;
tail: RequestQueue; msg: ROPE;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
IF tail = NIL
THEN tail ← queueHead ← NEW[RequestQueueRep]
ELSE {
DO
IF tail.next = NIL THEN EXIT;
tail ← tail.next;
ENDLOOP;
tail.next ← NEW[RequestQueueRep]; tail ← tail.next;
};
requestNumber ← reqNumbertail.number ← (reqNumber + 1) MOD requestLimit;
tail.request ← request;
tail.status ← waiting;
msg ← request.fileName;
IF request.copies # 1 THEN
msg ← IO.PutFR["%g (%g copies)", IO.rope[msg], IO.card[request.copies]];
InternalLogMessage[msg, requestNumber, request.requestor];
Save[];
NOTIFY newRequest;
};
LogMessage: PUBLIC ENTRY PROC [message: ROPE, requestNumber: INT ← -1, userName: ROPENIL, printCR: BOOLTRUE] = {
InternalLogMessage[message, requestNumber, userName, printCR];
};
reprintCopies: CARDINAL ← 0;
reprintCancelled: BOOLEANFALSE;
Reprint: PUBLIC ENTRY PROC [copies: CARDINAL] = {
reprintCancelled ← FALSE;
reprintCopies ← reprintCopies + copies;
NOTIFY newRequest;
};
CancelReprint: PUBLIC ENTRY PROC = {
reprintCancelled ← TRUE;
reprintCopies ← 0;
};
ReprintCancelled: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN [reprintCancelled]};
printingSuspended: BOOLEANFALSE;
GetSuspended: PUBLIC ENTRY PROC RETURNS [BOOLEAN] = {RETURN [printingSuspended]};
SetSuspended: PUBLIC ENTRY PROC [suspended: BOOLEAN] RETURNS [old: BOOLEAN] = {
old ← printingSuspended;
printingSuspended ← suspended;
NOTIFY newRequest;
};
abortCurrent: REF BOOLNEW[BOOLFALSE];
FindRequest: PROC [requestNumber: CARDINAL] RETURNS [request: Request, status: RequestStatus] = {
ENABLE UNWIND => NULL;
tail: RequestQueue;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
IF tail = NIL THEN EXIT;
IF tail.number = requestNumber THEN RETURN [tail.request, tail.status];
tail ← tail.next;
ENDLOOP;
RETURN [[NIL, NIL, NIL, NIL, NIL, 0], notFound];
};
GetFirstRequest: ENTRY PROC [] RETURNS [requestNumber: INT, copies: CARDINAL, request: Request] = {
ENABLE UNWIND => NULL;
tail: RequestQueue;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
WHILE printingSuspended OR (queueHead = NIL AND reprintCopies = 0) DO WAIT newRequest ENDLOOP;
abortCurrent^ ← FALSE;
reprintCancelled ← FALSE;
IF reprintCopies # 0 THEN {
requestNumber ← -1;
copies ← reprintCopies;
reprintCopies ← 0;
RETURN[requestNumber, copies, [NIL, NIL, NIL, NIL, NIL, copies]];
};
tail ← queueHead;
IF tail = NIL THEN ERROR;
RETURN [tail.number, tail.request.copies, tail.request];
};
RemoveRequest: ENTRY PROC [requestNumber: CARDINAL] = {
ENABLE UNWIND => NULL;
tail, oldTail: RequestQueue;
tail ← queueHead;
oldTail ← queueHead;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
IF tail = NIL THEN EXIT;
IF tail.number = requestNumber THEN {
IF tail.status = printing THEN abortCurrent^ ← TRUE;
IF tail = queueHead THEN queueHead ← tail.next ELSE oldTail.next ← tail.next;
EXIT;
};
oldTail ← tail;
tail ← tail.next;
ENDLOOP;
Save[];
};
SetStatus: ENTRY PROC [requestNumber: CARDINAL, status: RequestStatus] RETURNS [ok: BOOL] = BEGIN
tail: RequestQueue;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
IF tail = NIL THEN EXIT;
IF tail.number = requestNumber THEN {tail.status ← status; RETURN[TRUE]};
tail ← tail.next;
ENDLOOP;
RETURN [FALSE];
END;
DeferSaving: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL;
deferSaving ← TRUE;
};
DoDeferredSave: PUBLIC ENTRY PROC = {
ENABLE UNWIND => NULL;
deferSaving ← FALSE;
IF saveDeferred THEN Save[];
};
DoRequest: PUBLIC PROC [action: PROC [request: Request, requestNumber: CARDINAL, abort: REF BOOLEAN]] = {
request: Request;
requestNumber: INT;
copies: CARDINAL;
aborted: BOOLFALSE;
[requestNumber, copies, request] ← GetFirstRequest[];
IF requestNumber >= 0 THEN {
LogMessage["Started.", requestNumber, request.requestor];
[]← SetStatus[requestNumber, printing];
action[request, requestNumber, abortCurrent ! ABORTED => {aborted ← TRUE; CONTINUE}];
IF aborted THEN LogMessage["Aborted.", requestNumber, request.requestor]
ELSE LogMessage["Completed.", requestNumber, request.requestor];
RemoveRequest[requestNumber];
};
};
CancelRequest: PUBLIC PROC [requestNumber: CARDINAL] RETURNS [ok: BOOLEAN] = {
RemoveRequest[requestNumber];
RETURN [TRUE];
};
CheckRequest: PUBLIC PROC [requestNumber: CARDINAL,
action: PROC [request: Request, status: RequestStatus]] = {
status: RequestStatus ← notFound;
request: Request;
[request: request, status: status] ← FindRequest[requestNumber];
action[[request.fileName, request.requestTime, request.requestor, request.requestorPassword, request.separator, request.copies], status];
};
CountRequests: PUBLIC ENTRY PROC RETURNS [requestCount: NAT] = {
tail: RequestQueue ← queueHead;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
requestCount ← 0;
DO
IF tail = NIL THEN EXIT;
requestCount ← requestCount + 1;
tail ← tail.next;
ENDLOOP;
};
EnumerateRequests: PUBLIC ENTRY PROC [action: PROC [requestNumber: CARDINAL, request: Request, status: RequestStatus] RETURNS [continue: BOOLEAN]] = {
tail: RequestQueue ← NIL;
requestNumber: CARDINAL;
IF queueHead = NIL THEN Restore[];
tail ← queueHead;
DO
continue: BOOLEANTRUE;
myAction: PROC [request: Request, status: RequestStatus] = {
IF status = printing OR status = waiting THEN continue ← action[requestNumber, request, status];
};
IF tail = NIL THEN EXIT;
CheckRequest[(requestNumber ← tail.number), myAction];
IF NOT continue THEN EXIT;
tail ← tail.next;
ENDLOOP;
};
RegisterDisk: PUBLIC ENTRY PROC [file: ROPENIL] = {diskFileName ← file};
usingDisk: PROCESSNIL;
Records the process using the disk under the monitor, to avoid deadlocks in case of disk errors.
MsgList: TYPE ~ REF MsgListRep;
MsgListRep: TYPE ~ RECORD [
next: MsgList ← NIL,
msg: ROPENIL];
msgHead: MsgList ← NIL;
msgTail: MsgList ← NIL;
msgCnt: CARDINAL ← 0;
InternalLogMessage: INTERNAL PROC [message: ROPE, requestNumber: INT ← -1, userName: ROPENIL, printCR: BOOLTRUE] = {
scratch: REF TEXT ~ RefText.ObtainScratch[100];
text: REF TEXT ← scratch;
msg: ROPENIL;
IF msgHead = NIL THEN msgHead ← msgTail ← NEW[MsgListRep]
ELSE {msgTail.next ← NEW[MsgListRep]; msgTail ← msgTail.next};
IF showTimes THEN {
text ← TimeToRefText[BasicTime.Now[], text];
};
text ← RefText.AppendChar[text, ' ];
IF requestNumber >= 0 THEN {
text ← RefText.AppendChar[text, 'R];
text ← RefText.AppendChar[text, '#];
text ← Convert.AppendInt[to: text, from: requestNumber];
text ← RefText.AppendChar[text, ' ];
};
text ← RefText.AppendChar[text, '\t];
text ← RefText.AppendRope[text, userName];
text ← RefText.AppendChar[text, '\t];
text ← RefText.AppendRope[text, message];
IF printCR OR showTimes THEN {
text ← RefText.AppendChar[text, '\n];
};
msg ← Rope.FromRefText[text];
msgTail.msg ← msg; msgCnt ← msgCnt + 1;
IF serverStream # NIL THEN {IO.PutRope[serverStream, msg]; IO.Flush[serverStream]};
IF msgCnt > 150 THEN BEGIN
IF msgHead.next = NIL THEN {msgCnt ← 1; RETURN};
msgCnt ← msgCnt - 1;
msgHead ← msgHead.next;
END;
RefText.ReleaseScratch[scratch];
};
monthName: ARRAY BasicTime.MonthOfYear OF Rope.ROPE ~ ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???"];
TimeToRefText: PROC [time: BasicTime.GMT ← BasicTime.nullGMT, text: REF TEXT] RETURNS [t: REF TEXT] = BEGIN
unpacked: BasicTime.Unpacked ← BasicTime.Unpack[(IF time = BasicTime.nullGMT THEN BasicTime.Now[] ELSE time)];
stream: IO.STREAMIO.TOS[text];
zoneChar: CHAR ← ' ;
IO.PutF[stream, "%2g-%g-%02g ", IO.int[unpacked.day], IO.rope[monthName[unpacked.month]], IO.int[unpacked.year MOD 100]];
IO.PutF[stream, "%2g:%02g:%02g", IO.int[unpacked.hour], IO.int[unpacked.minute], IO.int[unpacked.second]];
t ← IO.TextFromTOS[stream];
END;
loggingMessages: BOOLEANFALSE;
serverStream: IO.STREAMNIL;
RegisterTTY: PUBLIC ENTRY PROC [stream: IO.STREAM] = {serverStream ← stream};
SetLogState: PUBLIC ENTRY PROC [logging: BOOLEAN] RETURNS [old: BOOLEAN] = {
old ← loggingMessages;
loggingMessages ← logging;
};
EnumerateMessages: PUBLIC PROC [action: PROC[message: ROPE] RETURNS [continue: BOOLEAN]] = BEGIN
myTail: MsgList ← msgHead;
DO
IF myTail = NIL THEN RETURN;
IF NOT action[myTail.msg] THEN RETURN;
myTail ← myTail.next;
ENDLOOP;
END;
END.
CHANGE LOG
Rick Beach, December 4, 1985 8:12:06 pm PST
changes to: RemoveRequest fixed incorrect removal of request at head of queue