Copyright (C) 1994, Digital Equipment Corp. MODULE-- callback handling -- when we need to call the freeProc for a Completion.T, we do it in another thread to avoid locking problems. This thread is started the first time we need to call a freeProc. Its creation is protected by /mu/. Completions waiting for their callbacks to be called are put into the Sequence /seq/ which is also protected by /mu/; IMPORT CompletionSeq, Picture, Thread; REVEAL T = Public BRANDED OBJECT cond : Thread.Condition := NIL; count : CARDINAL := 0; next : T := NIL; (* for free list *) freeProc : Picture.FreeProc := NIL; freeParam: REFANY := NIL; OVERRIDES init := Init; inc := Inc; dec := Dec; isFree := IsFree; waitUntilFree := WaitUntilFree; END; PROCEDURE Completion Init (c : T; initialCount := 1; freeProc : Picture.FreeProc := NIL; freeParam : REFANY := NIL ): T = BEGIN IF c.cond = NIL THEN c.cond := NEW(Thread.Condition); END; c.count := initialCount; c.freeProc := freeProc; c.freeParam := freeParam; RETURN c; END Init; PROCEDUREInc (c: T) = BEGIN LOCK c DO INC(c.count); END; END Inc; PROCEDUREDec (c: T) = VAR signal := FALSE; BEGIN LOCK c DO CASE c.count OF | 0 => RETURN; | 1 => DEC(c.count); signal := TRUE; ELSE DEC(c.count); END; END; IF signal THEN IF c.freeProc # NIL THEN SetupCallback(c) ELSE Thread.Broadcast(c.cond); END; END; END Dec; PROCEDUREIsFree (c: T): BOOLEAN = BEGIN RETURN c.count = 0; END IsFree; PROCEDUREWaitUntilFree (c: T) RAISES {Thread.Alerted} = BEGIN LOCK c DO WHILE c.count > 0 DO Thread.AlertWait(c, c.cond); END; END; END WaitUntilFree; PROCEDURENew (): T = VAR res: T := NIL; BEGIN LOCK freeMu DO IF free # NIL THEN res := free; free := res.next; END; END; IF res = NIL THEN res := NEW(T); END; RETURN res; END New; PROCEDUREDispose (c: T) = BEGIN <* ASSERT c.count = 0 *> LOCK freeMu DO c.next := free; free := c; END; END Dispose; VAR freeMu := NEW(MUTEX); free : T := NIL;
VAR mu := NEW(MUTEX); thread : Thread.T := NIL; seq := NEW(CompletionSeq.T).init(); seqNotEmpty := NEW(Thread.Condition);start the callback thread if necessary. Add the T to its input queue
PROCEDUREpull Ts off the queue and call each callback and notify any other threads waiting for the TSetupCallback (c: T) = VAR signal := FALSE; BEGIN LOCK mu DO IF thread = NIL THEN thread := Thread.Fork(NEW(Thread.Closure, apply := Apply)); END; seq.addhi(c); IF seq.size() = 1 THEN signal := TRUE; END; END; IF signal THEN Thread.Signal(seqNotEmpty); END; END SetupCallback;
PROCEDUREApply (<*UNUSED*>cl: Thread.Closure): REFANY = VAR c: T; BEGIN LOOP LOCK mu DO WHILE seq.size() = 0 DO Thread.Wait(mu, seqNotEmpty); END; c := seq.remlo(); END; <* ASSERT c.freeProc # NIL *> c.freeProc(c.freeParam); Thread.Broadcast(c.cond); Dispose(c); END; END Apply; BEGIN END Completion.