DIRECTORY Basics, ForkOps, ForkOpsFriends, Process, UnsafePropList, Xl, XlTQPrivate; XlImplTQ: CEDAR MONITOR LOCKS tq USING tq: TQ IMPORTS Basics, ForkOps, ForkOpsFriends, Process EXPORTS Xl, XlTQPrivate = BEGIN OPEN Xl, XlTQPrivate; <>TQRep: PUBLIC TYPE = XlTQPrivate.TQRec; <>TQ: TYPE = REF XlTQPrivate.TQRec; Wedged: PUBLIC ERROR = CODE; EvBool: TYPE = PACKED ARRAY [0..127] OF BOOL ¬ ALL[FALSE]; vanilla1: REF EvBool ¬ NEW[EvBool]; vanilla2: REF EvBool ¬ NEW[EvBool]; vanilla3: REF EvBool ¬ NEW[EvBool]; desperate1: REF EvBool ¬ NEW[EvBool]; desperate2: REF EvBool ¬ NEW[EvBool ¬ ALL[TRUE]]; ForgetQueuedElements: INTERNAL PROC [tq: TQ] = { Pass: INTERNAL PROC [vanilla: REF EvBool] = { lag: Job ¬ NIL; p: Job ¬ tq.next; IF p=NIL THEN RETURN; WHILE p#tq.tail DO IF p.event#NIL AND vanilla[Basics.BITAND[p.event.originalCodeByte, 127]] THEN { removed ¬ removed + 1; tq.eventCount ¬ tq.eventCount - 1; IF lag=NIL THEN tq.next ¬ p.next ELSE lag.next ¬ p.next; IF removed>10 THEN RETURN; } ELSE { lag ¬ p; }; p ¬ p.next ENDLOOP; }; removed: CARD ¬ 0; Pass[vanilla1]; IF removed>10 THEN RETURN; Pass[vanilla2]; IF removed>10 THEN RETURN; Pass[vanilla3]; IF removed>10 THEN RETURN; Pass[desperate1]; IF removed>10 THEN RETURN; Pass[desperate2]; }; RemoveErrorEvents: PUBLIC ENTRY PROC [tq: TQ] = { ENABLE UNWIND => NULL; WHILE tq.next#NIL DO WITH tq.next.event SELECT FROM error: ErrorNotifyEvent => { tq.eventCount ¬ tq.eventCount-1; tq.next ¬ tq.next.next; IF tq.next=NIL THEN tq.tail ¬ NIL }; ENDCASE => RETURN; ENDLOOP; }; globalLockTq: TQ ¬ NEW[TQRep]; --need the lock sHigh, sLow: INT ¬ 0; CreateTQ: PUBLIC PROC [createData: REF ¬ NIL, order: INT ¬ 0, reverse: BOOL ¬ FALSE] RETURNS [tq: TQ] = { Protected: ENTRY PROC [tq: TQ--lock!--, realTq: TQ] = INLINE { IF sLow=LAST[INT] THEN {sLow ¬ 0; sHigh ¬ sHigh+1}--sHigh doesn't overflow for 100 years ELSE {sLow ¬ sLow+1}; realTq.seq1 ¬ sHigh; realTq.seq2 ¬ sLow; }; tq ¬ NEW[TQRep ¬ [createData: createData, order: order]]; Protected[globalLockTq, tq]; IF reverse THEN { tq.seq1 ¬ - tq.seq1; tq.seq2 ¬ - tq.seq2; } }; InternalNewJob: INTERNAL PROC [tq: TQ] RETURNS [job: Job] = INLINE { job ¬ tq.free; IF job#NIL THEN {tq.free ¬ job.next; job.next ¬ NIL; tq.freeCnt ¬ tq.freeCnt-1} ELSE job ¬ NEW[JobRec]; }; InternalFreeJob: INTERNAL PROC [tq: TQ, job: Job] = INLINE { job.data ¬ NIL; --garbage collect now and not only when reused IF tq.freeCnt<25 THEN { job.next ¬ tq.free; tq.free ¬ job; tq.freeCnt ¬ tq.freeCnt + 1 }; }; EntryEnqueue: ENTRY PROC [tq: TQ, proc: EventProcType, data: REF, event: Event] RETURNS [mustFork: BOOL ¬ FALSE] = INLINE { job: Job ~ InternalNewJob[tq]; IF tq.dead THEN RETURN; job.data ¬ data; job.proc ¬ proc; job.event ¬ event; IF tq.next=NIL THEN { tq.eventCount ¬ 1; tq.next ¬ tq.tail ¬ job; IF ~tq.selfRunning THEN { tq.selfRunning ¬ TRUE; mustFork ¬ TRUE; } } ELSE { tq.eventCount ¬ tq.eventCount + 1; tq.tail.next ¬ job; tq.tail ¬ job; IF tq.eventCount>tq.upperLimit THEN ForgetQueuedElements[tq]; }; }; Enqueue: PUBLIC PROC [tq: TQ, proc: EventProcType, data: REF, event: Event] = { p: EventProcType ~ proc; --checks against local procedures IF EntryEnqueue[tq, p, data, event].mustFork THEN ForkOps.Fork[DispatcherProcess, tq, tq.priority]; }; SignalFor2Phase: ENTRY PROC [tq: TQ] = INLINE { IF tq.lockWaiterCount>0 AND tq.lockProcess=NIL THEN BROADCAST tq.lockWaiterCond }; DispatcherProcess: PROC [t: REF] = { tq: TQ ~ NARROW[t]; EntryNext: ENTRY PROC [tq: TQ, oldJob: Job] RETURNS [job: Job] = INLINE { IF oldJob#NIL THEN InternalFreeJob[tq, oldJob]; job ¬ tq.next; IF job=NIL THEN { tq.selfRunning ¬ FALSE; tq.tail ¬ NIL; tq.selfProcess ¬ NIL; IF tq.lockWaiterCount>0 AND tq.lockProcess=NIL THEN BROADCAST tq.lockWaiterCond; RETURN }; tq.eventCount ¬ tq.eventCount-1; tq.next ¬ job.next; WHILE tq.lockProcess#NIL DO tq.lockWaiterCount ¬ tq.lockWaiterCount+1; WAIT tq.lockWaiterCond; tq.lockWaiterCount ¬ tq.lockWaiterCount-1; ENDLOOP; tq.this ¬ job; }; job: Job; tq.selfProcess ¬ CurrentProcess[]; DO IF tq.lockWaiterCount>0 AND tq.lockProcess=NIL THEN { SignalFor2Phase[tq]; Process.Yield[]; }; job ¬ EntryNext[tq, job]; IF job=NIL THEN RETURN; job.proc[tq: tq, event: job.event, clientData: job.data <>]; tq.this ¬ NIL; ENDLOOP; }; AbortNotification: PROC [t: REF] = { tq: TQ ~ NARROW[t]; tq.this ¬ NIL; SignalFor2Phase[tq]; Process.Yield[]; }; IsTQ: PUBLIC PROC [x: REF ANY] RETURNS [b: BOOL] = { b ¬ x#NIL AND ISTYPE[x, TQ] }; NarrowTQ: PUBLIC PROC [x: REF ANY] RETURNS [t: TQ] = { t ¬ NARROW[x, TQ] }; CurrentProcess: PROC RETURNS [PROCESS] = TRUSTED INLINE { RETURN [LOOPHOLE[Process.GetCurrent[]]]; }; OwnsLock: PUBLIC PROC [tq: TQ] RETURNS [BOOL] = { self: PROCESS = CurrentProcess[]; RETURN [ self=tq.selfProcess OR self=tq.lockProcess ] }; GetLockOrderNum: PUBLIC PROC [tq: TQ] RETURNS [INT] = { RETURN [ tq.order ] }; GetLockOrder: PUBLIC PROC [first, second: TQ] RETURNS [l: LockOrder] = { l ¬ SELECT TRUE FROM first.dead OR second.dead => dead, first.order ok, first.order>second.order => reversed, first.seq1 ok, first.seq1>second.seq1 => reversed, first.seq2 ok, first.seq2>second.seq2 => reversed, first=second => ok, ENDCASE => ERROR; }; CallWithLocks: PUBLIC PROC [proc: PROC, tq1, tq2: TQ] = { self: PROCESS = CurrentProcess[]; IF tq1=NIL OR self=tq1.selfProcess OR self=tq1.lockProcess THEN { CallWithLock[tq2, proc]; RETURN }; IF tq2=NIL OR self=tq2.selfProcess OR self=tq2.lockProcess OR tq1=tq2 THEN { CallWithLock[tq1, proc]; RETURN }; SELECT GetLockOrder[tq1, tq2] FROM ok => {}; reversed => {t: TQ ¬ tq1; tq1 ¬ tq2; tq2 ¬ t}; ENDCASE => ERROR Wedged; DO IF TryAquireLock[tq1, self] THEN { IF TryAquireLock[tq2, self] THEN { proc[! UNWIND => { ReleaseLock[tq2]; ReleaseLock[tq1]; }]; ReleaseLock[tq2]; ReleaseLock[tq1]; RETURN } ELSE { ReleaseLock[tq1]; WaitForFreeOnce[tq2, self] } } ELSE { WaitForFreeOnce[tq1, self] }; ENDLOOP; }; WaitForFreeOnce: ENTRY PROC [tq: TQ, self: PROCESS] = { IF tq.this#NIL OR (tq.lockProcess#NIL AND tq.lockProcess#self) THEN { tq.lockWaiterCount ¬ tq.lockWaiterCount +1; WAIT tq.lockWaiterCond; tq.lockWaiterCount ¬ tq.lockWaiterCount -1; }; }; TryAquireLock: ENTRY PROC [tq: TQ, self: PROCESS] RETURNS [BOOL ¬ FALSE] = { IF tq.this#NIL THEN RETURN [FALSE]; IF tq.lockProcess=NIL THEN {tq.lockCount ¬ 1; tq.lockProcess ¬ self; RETURN [TRUE]}; IF tq.lockProcess=self THEN {tq.lockCount ¬ tq.lockCount+1; RETURN [TRUE]}; }; ReleaseLock: ENTRY PROC [tq: TQ] = { tq.lockCount ¬ tq.lockCount-1; IF tq.lockCount=0 THEN { tq.lockProcess ¬ NIL; IF tq.lockWaiterCount>0 THEN BROADCAST tq.lockWaiterCond }; }; CallWithLock: PUBLIC PROC [tq: TQ, proc: PROC] = { self: PROCESS = CurrentProcess[]; IF tq=NIL OR self=tq.selfProcess OR self=tq.lockProcess THEN {proc[]; RETURN}; IF tq.dead THEN ERROR Wedged; DO IF TryAquireLock[tq, self] THEN { proc[! UNWIND => ReleaseLock[tq]]; ReleaseLock[tq]; RETURN; }; WaitForFreeOnce[tq, self] ENDLOOP; }; SetTQPriority: PUBLIC PROC [tq: TQ, priority: Process.Priority] = { tq.priority ¬ priority; }; SetTQReadiness: PUBLIC PROC [tq: TQ, ms: INT] = TRUSTED { }; vanilla1[ORD[EventCode[motionNotify]]] ¬ TRUE; vanilla2[ORD[EventCode[motionNotify]]] ¬ TRUE; vanilla2[ORD[EventCode[graphicsExposure]]] ¬ TRUE; vanilla2[ORD[EventCode[noExposure]]] ¬ TRUE; vanilla2[ORD[EventCode[visibilityNotify]]] ¬ TRUE; vanilla3[ORD[EventCode[expose]]] ¬ TRUE; vanilla3­ ¬ vanilla2­; vanilla3[ORD[EventCode[enterNotify]]] ¬ TRUE; vanilla3[ORD[EventCode[leaveNotify]]] ¬ TRUE; desperate1­ ¬ vanilla3­; desperate1[ORD[EventCode[focusOut]]] ¬ TRUE; desperate1[ORD[EventCode[focusIn]]] ¬ TRUE; desperate1[ORD[EventCode[keyPress]]] ¬ TRUE; desperate1[ORD[EventCode[keyRelease]]] ¬ TRUE; desperate1[ORD[EventCode[buttonPress]]] ¬ TRUE; desperate1[ORD[EventCode[buttonRelease]]] ¬ TRUE; desperate2[ORD[EventCode[local]]] ¬ FALSE; ForkOpsFriends.RegisterAbortNotifier[DispatcherProcess, AbortNotification]; END. &XlImplTQ.mesa Copyright Σ 1988, 1989, 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved. Christian Jacobi, April 19, 1988 9:11:59 am PDT Christian Jacobi, August 13, 1993 12:18 pm PDT --the tq lock might be called inside the connection lock --prevents using up all storage for a blocked thread --remove --keep --first try to get rid of vanilla stuff --removes all leading error events TRUSTED {Process.InitializeCondition[@tq.newEvent, Process.MsecToTicks[100]]}; --old optimization -- outer loop allows inner loop without catch phrase for ENABLE ABORTED -- if it doesn't crash the outer loop is executed just once --new optimization -- Use ForkOpsFriends to be told about aborts without any enabling... --check 2 phase locking --DO ENABLE ABORTED => { --protect from abort in the clients proc (it won't crash anywhere else) tq.this ¬ NIL; SignalFor2Phase[tq]; Process.Yield[]; }; --ENDLOOP; --But no gurantee that it is free afterwards --Returns TRUE on success Process.SetTimeout[@tq.newEvent, Process.MsecToTicks[ms]] -- -- -- -- -- Κ–(cedarcode) style•NewlineDelimiter ˜code™ Kšœ ΟeœO™ZKšœ/™/K™.—K˜šΟk œ˜ K˜JK˜—š Οnœžœžœžœžœž˜-Kšœ8™8Kšžœ)˜0Kšžœ˜—šžœžœ˜K˜—Kšœžœžœ˜.Kšœžœžœžœ˜(K˜KšŸœžœžœžœ˜K˜Kšœžœžœžœ žœžœžœžœ˜:K˜Kšœ žœ žœ ˜#Kšœ žœ žœ ˜#Kšœ žœ žœ ˜#Kšœ žœ žœžœ˜%Kš œ žœ žœ žœžœ˜1K˜šŸœžœžœžœ˜0KšΟc4™4šŸœžœžœ žœ ˜-Kšœ žœ˜K˜Kšžœžœžœžœ˜šžœ ž˜šžœ žœžœžœ!˜Išžœ˜Kš ™Kšœ˜Kšœ"˜"šžœžœ˜ Kšžœ˜Kšžœ˜—Kšžœ žœžœ˜K˜—šžœ˜Kš ™Kšœ˜Kšœ˜——Kšœ ˜ Kšžœ˜—K˜—Kšœ žœ˜Kš '™'Kšœžœ žœžœ˜*Kšœžœ žœžœ˜*Kšœžœ žœžœ˜*Kšœžœ žœžœ˜,K˜K˜—K˜š Ÿœžœžœžœžœ˜1Kšžœžœžœ˜Kšœ"™"šžœ žœž˜šžœžœž˜šœ˜Kšœ ˜ Kšœ˜Kšžœ žœžœ ž˜!Kšœ˜—Kšžœžœ˜—Kšžœ˜—Kšœ˜—K˜Kšœžœžœ  ˜.Kšœ žœ˜K˜šŸœžœžœžœžœ žœžœžœžœžœ˜iš Ÿ œžœžœž  œ žœžœ˜>šžœžœžœ˜Kšžœ &˜FKšžœ˜—Kšœ(˜(Kšœ˜—Kšœžœ1˜9KšžœG™NK˜šžœ žœ˜Kšœ˜Kšœ˜K˜—Kšœ˜K˜—š Ÿœžœžœžœžœžœ˜DK˜šžœžœ˜ Kšžœ!žœ˜DKšžœžœ ˜—K˜—K˜š Ÿœžœžœžœžœ˜šžœžœ˜K˜K˜K˜K˜—Kšœ˜K˜—šŸ œžœžœžœžœžœ žœžœžœ˜{K˜Kšžœ žœžœ˜K˜K˜K˜šžœ žœ˜šžœ˜Kšœ˜K˜šžœžœ˜Kšœžœ˜Kšœ žœ˜K˜—K˜—šžœ˜Kšœ"˜"K˜K˜Kšžœžœ˜=K˜——K˜K˜—š Ÿœžœžœžœžœ˜OKšœ !˜;šžœ+žœ˜2K˜1—K˜—K˜š Ÿœžœžœžœžœ˜/šžœžœž˜3Kšž œ˜—K˜K˜—šŸœžœžœ˜$Kšœžœžœ˜Kš ™Kš G™GKš ;™;Kš ™K™Eš Ÿ œžœžœžœžœžœ˜IKšžœžœžœ˜/K˜šžœžœžœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜Kšžœžœžœž œ˜QKšž˜K˜—K˜ K˜K™šžœžœž˜K˜*Kšžœ˜K˜*Kšžœ˜—K˜K˜—K˜ Kšœ"˜"™šžœžœ™Kš $œ ™GKšœ žœ™K™K™K™—šž˜šžœžœžœžœ˜5K˜K˜K˜—K˜Kšžœžœžœžœ˜K˜JKšœ žœ˜Kšžœ˜—K™ —K˜—K˜šŸœžœžœ˜$Kšœžœžœ˜Kšœ žœ˜K˜K˜K˜—K˜šŸœžœžœžœžœžœžœ˜4Kš œžœžœžœžœ˜Kšœ˜K˜—šŸœžœžœžœžœžœžœ˜6Kšœžœžœ˜Kšœ˜K˜—š Ÿœžœžœžœžœžœ˜9Kšžœžœ˜(K˜K˜—š Ÿœžœžœžœžœžœ˜1Kšœžœ˜!Kšžœžœ˜5Kšœ˜K™—š Ÿœžœžœžœžœžœ˜7Kšžœ ˜Kšœ˜K™—š Ÿ œžœžœžœžœ˜Hšœžœžœž˜Kšœ žœ˜"Kšœ˜Kšœ%˜%Kšœ˜Kšœ#˜#Kšœ˜Kšœ#˜#Kšœ˜Kšžœžœ˜—Kšœ˜K™—š Ÿ œžœžœžœ žœ˜9Kšœžœ˜!š žœžœžœžœžœ˜AKšœž˜K˜—š žœžœžœžœžœ žœ˜LKšœž˜K˜—šžœž˜"Kšœ ˜ Kšœžœ˜.Kšžœžœ˜—– "cedar" stylešž˜– "cedar" stylešžœ˜– "cedar" stylešžœ˜šžœ˜šžœ˜šœžœ˜J˜J˜J˜—J˜J˜Jšž˜J˜—šžœ˜J˜J˜J˜——J˜—šžœ˜J˜J˜——K– "cedar" stylešžœ˜—K˜—˜K˜—š Ÿœžœžœžœžœ˜7J™,š žœ žœžœžœžœžœ˜EJ˜+Kšžœ˜J˜+J˜—K˜K˜—šŸ œžœžœžœžœžœžœžœ˜LJ™K– "cedar" styleš žœ žœžœžœžœ˜#K– "cedar" styleš žœžœžœ+žœžœ˜UK– "cedar" stylešžœžœ!žœžœ˜LK˜K˜—šŸ œžœžœžœ˜$K– "cedar" style˜ – "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœžœž œ˜8K– "cedar" style˜—K˜K˜—– "cedar" styleš Ÿ œžœžœžœžœ˜2Kšœžœ˜!Kš žœžœžœžœžœ žœ˜NKšžœ žœžœ˜– "cedar" stylešž˜– "cedar" stylešžœžœ˜!Jšœžœ˜"K– "cedar" style˜K– "cedar" stylešžœ˜K– "cedar" style˜—J˜K– "cedar" stylešžœ˜—K– "cedar" style˜—K˜šŸ œžœžœžœ!˜CKšœ˜Kšœ˜K˜—š Ÿœžœžœžœžœžœ˜9Kšœ9™9Kšœ˜K˜—Kšœ žœžœ˜.K™Kšœ žœžœ˜.Kšœ žœ!žœ˜2Kšœ žœžœ˜,Kšœ žœ!žœ˜2Kšœ žœžœ˜(K™K˜Kšœ žœžœ˜-Kšœ žœžœ˜-K™K˜Kšœ žœžœ˜,Kšœ žœžœ˜+Kšœ žœžœ˜,Kšœ žœžœ˜.Kšœ žœžœ˜/Kšœ žœžœ˜1K™Kšœ žœžœ˜*K™K˜KKšžœ˜K˜K˜—…—<2q