<> DIRECTORY Generator USING [Handle, HandleRecord], Process USING [Detach] ; GeneratorImpl: CEDAR MONITOR LOCKS private USING private: REF HandlePrivateRecord IMPORTS Process EXPORTS Generator = BEGIN OPEN Generator; HandlePrivateRecord: PUBLIC TYPE = MONITORED RECORD[ enumerator: PROC [self: Handle], next: REF ANY _ NIL, nextReady: CONDITION, getNext: CONDITION, nextNowReady: BOOLEAN _ FALSE, terminated: BOOLEAN _ TRUE ]; CreateGenerator: PUBLIC PROC[enumerator: PROC[self: Handle], clientData: REF ANY _ NIL] RETURNS[generator: Handle] = { generator _ NEW[HandleRecord _ [clientData: clientData, private: NEW[HandlePrivateRecord _ [enumerator: enumerator]]]]; ReStart[generator]; }; ReStart: PUBLIC PROC [self: Handle] = { private: REF HandlePrivateRecord _ self.private; IF ~private.terminated THEN Terminate[self]; private.nextNowReady _ FALSE; private.terminated _ FALSE; TRUSTED {Process.Detach[FORK ReStart2[self]]}; }; -- of ReStart ReStart2: PROC [self: Handle] = { private: REF HandlePrivateRecord _ self.private; Stop: ENTRY PROC [private: REF HandlePrivateRecord] = { IF ~private.terminated THEN -- enumerator stopped because it ran out, generate is still waiting. {private.terminated _ TRUE; NOTIFY private.nextReady}; -- generate will wake up, and return itself. <> }; private.enumerator[self ! ABORTED => CONTINUE; UNWIND => CONTINUE]; -- start the enumerator. On indicated errors, important to terminate the enumerator and detach the process. Stop[private]; -- enumerator now finished, either because it was told stop, via Terminate, or it ran out. In latter case, have to tell spelling corrector that nothing else is coming. }; Generate: PUBLIC PROC [self: Handle] RETURNS[value: REF ANY] = {private: REF HandlePrivateRecord _ self.private; Generate1: ENTRY PROC [private: REF HandlePrivateRecord] RETURNS[value: REF ANY] = { NOTIFY private.getNext; UNTIL private.nextNowReady OR private.terminated DO WAIT private.nextReady; ENDLOOP; value _ IF private.terminated THEN self ELSE private.next; private.nextNowReady _ FALSE; }; RETURN[Generate1[private]]; }; <> Terminate: PUBLIC PROC [self: Handle] = {private: REF HandlePrivateRecord _ self.private; Terminate1: ENTRY PROC [private: REF HandlePrivateRecord] = { IF private.terminated THEN RETURN; -- means enumerator has already stopped because it ran out. private.terminated _ TRUE; NOTIFY private.getNext; -- to get Produce to wake up and return TRUE, thereby telling enumerator to stop. }; Terminate1[private]; }; <> Produce: PUBLIC PROC [self: Handle, next: REF ANY] RETURNS [BOOLEAN] = {private: REF HandlePrivateRecord _ self.private; Produce1: ENTRY PROC [private: REF HandlePrivateRecord] = { private.next _ next; private.nextNowReady _ TRUE; NOTIFY private.nextReady; UNTIL ~private.nextNowReady OR private.terminated DO WAIT private.getNext; ENDLOOP; }; Produce1[private]; RETURN[private.terminated] }; END. testList: LIST OF REF ANY _ LIST[$Foo, $Fie, $Fum]; Test: PROC [self: Handle] = { FOR l: LIST OF REF ANY _ testList, l.rest UNTIL l = NIL DO IF Produce[self, l.first] THEN EXIT; ENDLOOP; }; Done: SIGNAL = CODE; Test1: PROC [self: Handle] = { proc: SAFE PROC [atom: ATOM] = {IF Produce[self, atom] THEN SIGNAL Done}; Atom.MapAtoms[proc ! Done => CONTINUE]; };