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
EXPORTS Generator
= BEGIN OPEN Generator;
HandlePrivateRecord: PUBLIC TYPE = MONITORED RECORD[
enumerator: PROC [self: Handle],
next: REF ANYNIL,
nextReady: CONDITION,
getNext: CONDITION,
nextNowReady: BOOLEANFALSE,
terminated: BOOLEANTRUE
];
CreateGenerator: PUBLIC PROC[enumerator: PROC[self: Handle], clientData: REF ANYNIL] 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 ANYLIST[$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];
};