DIRECTORY Process USING [Abort, Detach, EnableAborts, GetCurrent], RuntimeError USING [Resume, SendMsg], SharedErrors USING []; SharedErrorsImpl: CEDAR MONITOR LOCKS context USING context: SharedErrorContext IMPORTS Process, RuntimeError EXPORTS SharedErrors ~ BEGIN PList : TYPE = LIST OF PROC; SharedErrorContext: TYPE = REF SharedErrorContextObj; SharedErrorContextObj: TYPE = MONITORED RECORD[ processes: LIST OF ProcessContext, parentWakeup: CONDITION, childWakeup: CONDITION ]; PCList: TYPE = LIST OF ProcessContext; ProcessContext: TYPE = REF ProcessContextObj; ProcessContextObj: TYPE = RECORD [ process: PROCESS, error: SIGNAL ANY RETURNS ANY, args: POINTER, results: POINTER, done: BOOL ]; Fork: PUBLIC PROC [children: LIST OF PROC] ~ { context: SharedErrorContext ¬ NEW[SharedErrorContextObj]; InternalFork: ENTRY PROC [context: SharedErrorContext] ~ { ENABLE UNWIND => { alldone: BOOL ¬ FALSE; firstTime: BOOL ¬ TRUE; DO alldone ¬ TRUE; FOR tc: PCList ¬ context.processes, tc.rest UNTIL tc = NIL DO IF ~tc.first.done THEN TRUSTED { alldone ¬ FALSE; IF firstTime THEN Process.Abort[tc.first.process] }; ENDLOOP; IF alldone THEN EXIT; firstTime ¬ FALSE; WAIT context.parentWakeup; ENDLOOP; }; allDone: BOOL ¬ FALSE; FOR tp: PList ¬ children, tp.rest UNTIL tp = NIL DO newProcess: PROCESS; TRUSTED { UNCHECKED { newProcess ¬ FORK Stopper[context, tp.first] }}; context.processes ¬ CONS[ NEW[ProcessContextObj ¬ [newProcess, NIL, NIL, NIL, FALSE]], context.processes]; TRUSTED { Process.Detach[newProcess] }; ENDLOOP; UNTIL allDone DO WAIT context.parentWakeup; allDone ¬ TRUE; FOR tc: PCList ¬ context.processes, tc.rest UNTIL tc = NIL DO IF tc.first.error # NIL THEN TRUSTED { RaiseSignal[tc.first.error, tc.first.results, tc.first.args]; tc.first.error ¬ NIL; BROADCAST context.childWakeup; }; IF ~tc.first.done THEN allDone ¬ FALSE; ENDLOOP; ENDLOOP; }; TRUSTED { Process.EnableAborts[@context.childWakeup]; Process.EnableAborts[@context.parentWakeup] }; InternalFork[context]; }; Stopper: PROC [c: SharedErrorContext, p: PROC] ~ { FindProcessContext: INTERNAL PROC [p: UNSAFE PROCESS] RETURNS [pc: ProcessContext] ~ { FOR tc: PCList ¬ c.processes, tc.rest UNTIL tc = NIL DO IF tc.first.process = p THEN RETURN[tc.first] ENDLOOP; }; SetDone: ENTRY PROC [context: SharedErrorContext ¬ c] ~ { FindProcessContext[Process.GetCurrent[]].done ¬ TRUE; NOTIFY context.parentWakeup }; CatchAndWait: ENTRY PROC [context: SharedErrorContext ¬ c] ~ { ENABLE UNWIND => NULL; myId: UNSAFE PROCESS ¬ Process.GetCurrent[]; myContext: ProcessContext ¬ FindProcessContext[Process.GetCurrent[]]; TRUSTED { [args: myContext.args, signal: myContext.error, results: myContext.results] ¬ SIGNAL RuntimeError.SendMsg[] }; NOTIFY context.parentWakeup; WHILE myContext.error # NIL DO WAIT context.childWakeup ENDLOOP; }; { ENABLE { ABORTED => GOTO out; -- this is how an UNWIND in the parent gets reflected in the child UNWIND => NULL; -- shouldn't ever see one of these here but definitely don't ever want to catch one in the ANY arm ANY => TRUSTED {CatchAndWait[]; SIGNAL RuntimeError.Resume}; }; p[]; EXITS out => NULL }; SetDone[]; -- tell parent that it's over }; RaiseSignal: PROC [which: SIGNAL ANY RETURNS ANY, rtns: POINTER, args: POINTER] ~ TRUSTED MACHINE CODE { "XR_RaiseSignal" }; Init: PROC ~ { }; Init[]; END. Ζ SharedErrorsImpl.mesa Copyright Σ 1990, 1992 by Xerox Corporation. All rights reserved. Carl Hauser, October 31, 1987 1:34:59 pm PST Chauser, January 25, 1991 1:32 pm PST Willie-s, January 31, 1992 3:26 pm PST make sure that all the children are finished before letting this frame (and hence its caller) disappear the algorithm here could be converted to using a counter of completed processes but it seems useful, at least during debugging, to keep a record of the state of each of the children. beginning of InternalFork spawn the children and record them in the context unchecked because the procedures we were given may be internal procs. collect the children raise the error; enable aborts on childWakeup so that the child that suffered the error can be aborted as part of unwinding the parent; and on parentWakeup so the whole shebang can be aborted by aborting the parent. Each child process has an instance of Stopper at its root. body of Stopper ΚΩ–(cedarcode) style•NewlineDelimiter ™šœ™Jšœ Οeœ7™BIcodešœ,™,K™%K™&J™—šΟk ˜ Kšœžœ+˜8Kšœ žœ˜%Kšœ žœ˜K˜—Kš Πlnœžœžœžœ žœ˜OKšžœ˜Kšžœ ˜šœž˜K˜—Kš œžœžœžœžœ˜K˜Kšœžœžœ˜5šœžœž œžœ˜0Kšœ žœžœ˜"Kšœž œ˜Kšœ ž ˜K˜K˜—Kšœžœžœžœ˜&Kšœžœžœ˜-šœžœžœ˜"Kšœ žœ˜Kš œžœžœžœžœ˜Kšœžœ˜Kšœ žœ˜Kšœž˜ K˜—K˜š Οnœžœžœ žœžœžœ˜.Kšœžœ˜9š  œžœžœ"˜:šžœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜K™gK™Άšž˜Kšœ žœ˜šžœ)žœžœž˜=šžœžœžœ˜ Kšœ žœ˜Kšžœ žœ!˜2K˜—Kšžœ˜—Kšžœ žœžœ˜Kšœ žœ˜Kšžœ˜Kšžœ˜—K˜—Kšœ™Kšœ žœžœ˜K˜K™1šžœžœžœž˜3Kšœ žœ˜K™EKšžœž œžœ˜FKš œžœžœ"žœžœžœžœ˜lKšžœ ˜'Kšžœ˜—K˜K™šžœ ž˜Kšžœ˜Kšœ žœ˜šžœ)žœžœž˜=šžœžœžœžœ˜&Kšœ™K˜=Kšœžœ˜Kšž œ˜K˜—Kšžœžœ žœ˜'Kšžœ˜—Kšžœ˜—K˜—KšœΖ™Ζšžœ˜ Kšœ+˜+Kšœ+˜+Kšœ˜—K˜K˜K˜—š œžœžœ˜2Kšœ:™:Kšœ™š  œžœžœžœžœ˜Všžœ#žœžœž˜7Kšžœžœžœ ˜-Kšžœ˜—K˜K˜—š œžœžœ&˜9Kšœ0žœ˜5Kšžœ˜K˜K˜—š  œžœžœ&˜>Kšžœžœžœ˜Kšœžœžœ˜,K˜EKšžœRžœœ ˜yKšžœ˜šžœžœžœ˜Kšžœ˜Kšžœ˜—K˜K˜—KšœΟb™˜šžœ˜KšžœžœΟcB˜WKšžœžœ’b˜rKšžœžœžœ˜=K˜—K˜šž˜Kšœž˜ —K˜—Kšœ ’˜(K˜K˜—š  œžœ žœžœžœžœžœžœžœžœžœ˜hK˜Kšœ˜K˜—š œžœ˜K˜—K˜K˜K˜Kšžœ˜—…— ₯