<> <> <> <> DIRECTORY CoFork USING[], PrincOps USING[ControlLink, FrameHandle, NullFrame, Port, StateVector], PrincOpsUtils USING[GetReturnFrame, MyLocalFrame, SetReturnFrame]; CoForkImpl: PROGRAM IMPORTS PrincOpsUtils EXPORTS CoFork = BEGIN OPEN PrincOps, PrincOpsUtils; nCoForks: LONG CARDINAL _ 0; recentCoForkMeFrame: FrameHandle _ LOOPHOLE[0]; recentCallerFrame: FrameHandle _ LOOPHOLE[0]; recentConsumerFrame: FrameHandle _ LOOPHOLE[0]; CoForkMe: PUBLIC PROCEDURE RETURNS[--put--POINTER] = BEGIN NFramePointers: CARDINAL = 3; FrameList: ARRAY [0..NFramePointers) OF UNSPECIFIED _ [@portA, @portB, @consumersFrame]; portA: PORT; -- this port is called by producer (sink) (put) portB: PORT; -- this port is called by consumer (source) (get) stopper: PROCEDURE = -- this procedure is called by the consumer to stop the producer BEGIN ehSatterthwaiteMemorialLocal0: INTEGER; xNullRequest: StateVector; IF FALSE THEN ehSatterthwaiteMemorialLocal0_3183; xNullRequest _ STATE; nullRequest _ xNullRequest; consumerHasRequestedTermination _ TRUE; consumersFrame _ GetReturnFrame[]; SetReturnFrame[forkMeFrame]; -- now return to forkMe END; forkMeFrame: FrameHandle _ MyLocalFrame[]; caller: FrameHandle _ GetReturnFrame[]; callersCaller: ControlLink _ caller.returnlink; nullItem: StateVector; nullRequest: StateVector; throwAway: StateVector; consumerHasRequestedTermination: BOOLEAN _ FALSE; consumersFrame: FrameHandle _ LOOPHOLE[0]; fakedNoParameterReturnState: StateVector; <> nCoForks _ nCoForks + 1; recentCoForkMeFrame _ forkMeFrame; recentCallerFrame _ caller; recentConsumerFrame _ LOOPHOLE[callersCaller]; <> LOOPHOLE[portA, Port].dest _ LOOPHOLE[@portB]; LOOPHOLE[portA, Port].frame _ LOOPHOLE[0]; LOOPHOLE[portB, Port].dest _ LOOPHOLE[@portA]; LOOPHOLE[portB, Port].frame _ LOOPHOLE[0]; <> <> <> SetReturnFrame[NullFrame]; -- caller's frame is pointed to by "caller". set to nullFrame so that next line does not form a loop caller.returnlink _ LOOPHOLE[forkMeFrame]; -- caller's caller is pointed to by "callersCaller". <> LOOPHOLE[portB, Port].frame _ LOOPHOLE[0, FrameHandle]; LOOPHOLE[portA, Port].frame _ caller; LOOPHOLE[@portB, POINTER TO PORT[POINTER TO PORT]][@portA]; nullItem _ STATE; -- caller calls us back (through the sink port) with the value of a null item <> LOOPHOLE[callersCaller, PROCEDURE[POINTER TO PORT, PROCEDURE]][@portB, stopper]; throwAway _ STATE; -- if a producer returned, then we just received garbage return parameters <> IF consumerHasRequestedTermination THEN BEGIN -- the consumer called the stopper, so we want to feed the final consumer item to the producer until the producer returns <> caller.returnlink _ LOOPHOLE[@portB]; -- preceeding value of caller.returnLink was forkMeFrame, which is now known by the process since we are running WHILE LOOPHOLE[portA, Port].frame # LOOPHOLE[0, FrameHandle] DO nullRequest.source.frame _ NullFrame; nullRequest.dest.frame _ MyLocalFrame[]; TRANSFER WITH nullRequest; -- puts null request on stack (@portB)[]; throwAway _ STATE; ENDLOOP; <> fakedNoParameterReturnState.instbyte _ 0; fakedNoParameterReturnState.stkptr _ 0; fakedNoParameterReturnState.dest.frame _ consumersFrame; fakedNoParameterReturnState.source.frame _ NullFrame; END ELSE BEGIN -- the producer returned, so we want to feed the null item to the consumer until he calls the stopper WHILE NOT consumerHasRequestedTermination DO nullItem.source.frame _ NullFrame; nullItem.dest.frame _ MyLocalFrame[]; TRANSFER WITH nullItem; -- puts null item on stack LOOPHOLE[@portB, PROCEDURE][]; -- so that the return from stopper will not runinto a PortI throwAway _ STATE; ENDLOOP; <> IF LOOPHOLE[forkMeFrame, POINTER TO CARDINAL]^ = 0 THEN ERROR; fakedNoParameterReturnState.instbyte _ 0; fakedNoParameterReturnState.stkptr _ 0; fakedNoParameterReturnState.dest.frame _ consumersFrame; fakedNoParameterReturnState.source.frame _ NullFrame; END; <> SetReturnFrame[consumersFrame]; -- so that when the cells are deregisterd, the consumer is still accessable <> RETURN WITH fakedNoParameterReturnState; END; END. <> <> <> <> <> <> <> <> <> <> <> <<16-Oct-81 10:24:20: Cedar version; collection during lost frame time? Life is hard.>> <<1-Dec-81 15:01:32: change to allow registration of frame pointers so that all frames can always be found during garbage collection. Mechanism is to register with the Cedar Engine room a vector of pointers to cells that hold frame pointers. We choose to register all such cells in CoForkMe that hold such pointers and are not present just for debugging. This is a sufficient set of cells, otherwise CoForkMe could not work. (i.e. some dangling frame would never be used again. Two other issues must be addressed: 1) no loops in the frame pointers, and 2) no registered cell holds a pointer to a non existent cell. I believe there are no loops, after a discussion with Rovner and many pictures on the board. I Just examined the code and have made the appropriate changes to avoid pointers to non existent frames.>> <> <<(long) remark: 26-Mar-82 14:31:50>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <>