Generator package Edited by Teitelman on January 14, 1983 1:45 pm
DIRECTORY
Generator USING [Handle, HandleRecord],
Process USING [Detach]
;
GeneratorImpl: CEDAR MONITOR LOCKS private USING private: REF HandlePrivateRecord
IMPORTS Process
= 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.
and then detach the process. The corrector will wake up, finish, call Terminate, and it will notice that the enumerator has terminated and quite.
};
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]];
};
called when correction succeeds to tell enumerator to stop.
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];
};
for use inside of the enumerator. if returns TRUE, then client should do whatever is necessary to tell the enumerator to terminate.
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];
};