|
|
DataMuseum.dkPresents historical artifacts from the history of: Bogika Butler |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Bogika Butler Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 56064 (0xdb00)
Types: TextFile
Names: »UNIJCS.SA«
└─⟦311ba069f⟧ Bits:30009789/_.ft.Ibm2.50006625.imd Mogens Pelles Zilog 80,000 / EOS projekt
└─⟦this⟧ »UNIJCS.SA«
└─⟦49237ce80⟧ Bits:30009789/_.ft.Ibm2.50006627.imd Mogens Pelles Zilog 80,000 / EOS projekt
└─⟦this⟧ »UNIJCS.SA«
└─⟦714bbb381⟧ Bits:30009789/_.ft.Ibm2.50006595.imd Mogens Pelles Zilog 80,000 / EOS projekt
└─⟦this⟧ »UNIJCS.SA«
æ*****************************************************************
Copyright 1984 by
NCR Corporation
Dayton, Ohio U.S.A.
All Rights Reserved
******************************************************************
EOS Software produced by:
NCR Systems Engineering - Copenhagen
Copenhagen
DENMARK
*****************************************************************å
OBJECT PROGRAM UNIPS ;
æ U N I x P r o c e s s a n d S i g n a l m a n a g e m e n t .
UU UU N NN IIII PPPPP SSSS WRITTEN BY:
UU UU NN NN II PP PP SS SS
UU UU NNN NN II PP PP SS V I L H E L M
UU UU NNNNNN II PPPPP SSSS
UU UU NN NNN II PP SS R O S E N Q V I S T
UU UU NN NN II PP SS SS
UUUU NN N IIII PP SSSS N C R S E - C P
å CONST VersID=010100; VersDate=831110; æ
This EOS module implements process and signal functionality as expected by
C programs under UNIX version 7. (UNIX is a trade mark of BELL LABS.).
A system call library that calls the UNIPS interfaces must be linked with the
C programs.
When UNIPS is installed it imports references to the following objects:
Allocate, Scheduler, ObjDir and Clock.
When a terminal is included (ENTRY INCLUDE) a reference to a "share master
object" must be passed as a formal pointer parameter. The object will be called
during INCLUDE and must return a refrence (probably an ownset) to an INPUT/
OUTPUT family object. This object serves as the default file system for the
C programs called from the terminal. The default file system must support
the following non-arch. entries:
IOSYS.CloneEnv , IOSYS.WaitSignal and FAO.CloneFao .
The UNIPS module itself does not belong to a family. However, a job handler
family object is created each time INCLUDE is called.
å
æ$H=0 : no heap space å
æ$D- : this assignment is only valid for the first part of the program. å
æ pascal statement numbers are heavily used during debugging. å
æ$L-å
æ$F=FAMILY.UNIV.IDå
æ$F=FAMILY.KNEL.IDå
PROCEDURE PrintText(text:fullId); FORWARD;
PROCEDURE PrintVar(text:fullId; UNIV v:BlockPtr); FORWARD;
FUNCTION Statement : integer; FORWARD;
æ$F=FAMILY.ALLOC.IDå
æ$F=FAMILY.SCHED.IDå
æ$F=FAMILY.OBJDIR.IDå
æ$F=FAMILY.IOSYS.IDå
æ$L+å
æ$Eå
æ$F=PRIVATE.UAPPLI.IDå
æ$Eå
æ$F=FAMILY.CLOCK.IDå
æ$F=PRIVATE.SHCLFS.IDå
æ$Eå
æ$F=PRIVATE.UNIJCS.IDå
æ$Eå
æ$L+å
FUNCTION æ value data in eosPascal cannot be specified as a sub segment
in the actual list of objCall, callExtend etc..However, this
However, this would be more convenient when UNIPS
"forks" (entry fork) or "execs" (entry exec) a utility.
solutions: 1) extend eosPascal
2) change UNIPS: Use the WHILE construct to specify value params.
3) implement an assembly routine as specified here.
å
ObjCallExtend (VAR callExt : ref; VAR obj : ref; func : integer;
VAR valueSeg : ref; valueBytes : long;
VAR files : ref; VAR jobhandler : ref;
VAR æOUTå execArgs : ref;
æ optional å VAR stack : ref; æ optional å VAR data : ref)
: ResultType; FORWARD;
PROCEDURE PS (hint : fullId); æprint statement numberå
VAR s : integer;
BEGIN s:=statement; PrintVar(hint,s); END;
PROCEDURE PSS(hint : fullId; l : Long); æprint statement number and Longå
VAR v : RECORD s : Long; l : Long; END;
BEGIN v.s:= statement; v.l:= l; PrintVar(hint,v); END;
PROCEDURE PR ( res : ResultType); æprint Resultå
VAR OutPut : RECORD
StatNo : integer;
result : ResultType;
END;
BEGIN
OutPut.StatNo:= statement;
OutPut.result:= res;
PrintVar('UNIPS Number OrgSy Au Ar OrgNo Fa Ca',OutPut);
END;
æ$Eå
PROCEDURE SCheck ( R : ResultType);
BEGIN
IF R.main <> ok THEN
begin printtext('***SCheck E R R O R ***'); PR(R) end else
æ#ALRES# PR(R) &ALRES&å ;
END;
PROCEDURE NoCheck ( R : ResultType);
BEGIN
æ#ALRES# PR(R) &ALRES&å æ#BTRES# PR(R) &BTRES&å ;
END;
PROCEDURE RCheck ( R : ResultType);
BEGIN
æ#ALRES# PR(R) &ALRES&å ;
IF R.main <> ok THEN BEGIN
æ#BTRES# PR(R) &BTRES&å ; ObjReturn(R);
END;
END;
PROCEDURE XCheck ( R : ResultType);
BEGIN
æ#ALRES# PR(R) &ALRES&å ;
IF R.main <> ok THEN BEGIN
æ#BTRES# PR(R) &BTRES&å ; exception(R);
END;
END;
FUNCTION Call ( R : ResultType) : ResultType;
BEGIN
æ#ALRES# PR(R) &ALRES&å æ#BTRES# PR(R) &BTRES&å ;
Call := R ;
END;
FUNCTION univRes (m : byte) : ResultType;
VAR r : ResultType;
BEGIN r.family:=0; r.main:= -m; r.orgNo:=statement; univRes:=r; END;
æ$Eå
æ*********** U N I P S D A T A S T R U C T U R E S ************å
CONST
ignore=1; default=0; noOfSignals=15; noOfFiles=20;
SIGHUP=1; SIGINT=2; SIGILL=4; SIGTRAP=5; SIGKILL=9; SIGALRM=14;
MaxSeconds=1000; MaxUSeconds=1000000000; Ragnarok=LongMax;
stop=0; rej=1; æpropagation modeså
uxio=1; æunix io-modeå
SHIFT16=16#10000;
TYPE
textVar = charstring; æ ARRAY Æ1..AddrMaxÅ OF char; å
text = ^ ÆÅ textVar ;
dirWName= ARRAY Æ1..14Å OF char; æX:PROCID:INDEXå
sigItem = RECORD
sigState : Long; æ ignore, default, sigFunc å
sigPend : Boolean;
END;
divPDType = RECORD æ variables returned from newUproc å
procID : IDType;
noOfFiles: long;
fenvSize: sizeType;
extSize : sizeType;
END;
PstateType = ( embryo, aborted, child, waiting, exec, zombie, dead );
æ embryo : until FORKEND is called by process
aborted: killed before FORKEND is called
child : normal running state (after FORKEND)
waiting: proc is waiting for child termination
exec : after PREPEXEC, before call to new program
zombie : terminated, parent exists but has not waited
dead : terminated, no parent
å
procData = RECORD
procID : IDType;
progCount : word; æ #programs executed by this process å
noOfChildren: word; æ #forks executed by this process å
procState : PstateType;
childState : PstateType; æ child OR embryo å
termStatus : Byte; æ initially ZERO , when killed = signalID å
useNewExt : boolean; æ true if a new extention is created in PREPEXEC å
alarmTime : Long; æ <> 0 when proc is in the alarmSet å
exitRes : ResultType; æ result of some program call å
parentID : IDType;
zombieID : IDType; æ assigned by zombie before a å
zombieStatus: Long; æ waiting parent is signaled. å
callLoad : dirWName;
callCrea : dirWName;
execLoad : dirWName;
execCrea : dirWName;
valBytes : long; æ size of valuedata for this process å
extSize : sizeType; æ std. size of extension object å
sig : ARRAY Æ1..noOfSignalsÅ OF sigItem;
END;
uniData = RECORD
alarmTime : Long;
noOfAlarms: Word;
newTime : Long;
nxtProcID : IDType; æ initially = 2 å
nxtPipeID : IDType; æ initially = 0 å
maxStack : SizeType; æ "constant" set during INIT å
procSize : SizeType; æ "constant" set during INIT å
extSize : SizeType; æ "constant" set during INIT å
fenvSize : SizeType; æ "constant" set during INIT å
END;
kpt = ^^ ;
PprocData = ^^ procData;
PuniData = ^^ uniData;
fileEnv = ARRAY Æ1..noOfFilesÅ OF kpt;
PfileEnv = ^^ fileEnv;
PgrpObj = ^^ GroupObj;
PobjDir = ^^ objDir;
PprocEnv = ^^ procEnv;
PgrpEnv = ^^ grpEnv;
PuniEnv = ^^ uniEnv;
procEnv = RECORD
pData : PprocData ;æ embedded segment object å
fileOwn : ^^ ;æ owns the file envelope object å
callProg : ^^UnixUtil ;æ called program å
execProg : ^^UnixUtil ;æ next program å
execExt : ^^ ; æ simple ref to extension object å
egoObj : ^^ ; æ simple ref to process object å
egoEnv : PprocEnv ;æ simple ref to process envelope å
parent : PprocEnv ;æ simple ref to parent envelope å
forkStack : ^^ ; æ used byå
forkData : ^^ ; æ fork å
files : PfileEnv ;æ ref to file envelope å
alarmSet : PprocEnv ;æ next proc in alarmset OR undef å
waitCond : ^^Condition;æ used by PAUSE , WAIT and FORK å
zombCond : ^^Condition;æ queue of zombies å
ProcGrp : PgrpEnv ;æ group of process, used by alarmProc å
END;
grpEnv = RECORD
code : ^^ ;
gData : ^^ ;æ no group data at present å
dir : ^^objDir ;æ owns objDir of this groupå
ClSh : ^^IoSys ;æ clone/share group object å
grpGate : ^^Gate ;æ protects group and processes of this group å
tmpManSet : PprocEnv ;æ manset of attention process + work manset å
procSet : PprocEnv ;æ manset of normal processes å
procOwn : ^^ ; æ owns all processes in this group å
unix : puniEnv ;æ global pointers å
deadZombie : PprocEnv ;æ may point to the oldest zombie å
execExtO : ^^ ; æ owns extension objects while they are not used å
fenvSet : ^^ ; æ manset of file envelopes å
egoObj : PgrpObj;
egoEnv : PgrpEnv;
END;
uniEnv = RECORD æ global pointers and pointers of alarm process å
code : ^^ ;
uData : PuniData ;
dir : ^^objDir ;æ set when loaded å
alloc : ^^allocate;æ set when loaded å
sched : ^^Scheduler;æ set when loaded å
uniClock : ^^Clock ;æ set when loaded å
egoEnv : PuniEnv ;æ set when loaded å
alarmSet : PprocEnv ;æ first proc in alarmSet å
grpSet : ^^ ; æ manset of groupså
uniGate : ^^Gate ;æ protects global unips structures å
alarmCond : ^^Condition;æ Time Out for the alarm process å
grpOwn : ^^ ; æ owns group objects and alarm process å
END;
æ if uniGate and (one) grpGate must both be locked, unigate is locked first å
æ$Eå
æ$K+ : needed during debugging å
æ******************* G L O B A L P R O C E D U R E S *******************å
TYPE longName = array Æ1..50Å of char;
FUNCTION FIX(w:dirWName):longName; æ this procedure fixes an error in objDir å
VAR f : longName; i : integer;
BEGIN for i:= 1 to 14 do fÆiÅ:= wÆiÅ; fÆ15Å:= chr(0); FIX:= f; END;
FUNCTION WName (c : char; ID : IDType; Index : word) : dirWName;
VAR w : dirWName; i : integer ;
BEGIN
wÆ1Å:= c ; wÆ2Å:= ':' ; wÆ9Å:= ':' ;
FOR i:= 8 DOWNTO 3 DO BEGIN
wÆiÅ:= chr(ord('0') + (ID mod 10)) ;
ID:= ID div 10 ;
END ;
FOR i:= 14 DOWNTO 10 DO BEGIN
wÆiÅ:= chr(ord('0') + (Index mod 10)) ;
Index:= Index div 10 ;
END ;
WName:= w ;
END ;
PROCEDURE sigProc (VAR proc : PprocEnv;
VAR p : procData;
VAR dir : PobjDir ;
sigNo : Long );
æ send signal to process, grpGate has been locked by caller å
BEGIN
æ the procState of the proc will be child, exec or waiting, because
embryo is killed by parent and aborted+zombie+dead does not execute at allå
WITH item = p.sigÆsigNoÅ DO BEGIN
IF item.sigState <> ignore THEN BEGIN æ abort or speedUp å
IF ( item.sigState = default) OR ( item.sigPend ) THEN BEGIN
IF p.termStatus = 0 THEN p.termStatus := sigNo ;
NoCheck( dir.AbortObj(;p.callCrea)); æ sufficient stack ! å
END ELSE BEGIN æsigFunc defined å
item.sigPend := true ;
NoCheck( dir.SpeedUpObj(;p.callCrea)); æ sufficient stack ! å
æ speedUpObj may later be replaced by the kernel operation speedUpProc å
END ;
END æ don't ignore å;
END æWITH itemå;
END æsigProcå;
æ$Eå
FUNCTION sigGrp (VAR grp : PgrpEnv ; æ must be a direct address å
VAR proc : PprocEnv; æ working pointer å
Var noOfSignals : Long;
sigNo : Long ;
grpFunc : Long) : Boolean;
æ grpFunc = 0 => signal is sent to all processes in the group
grpFunc < 0 => signal is sent to all processes except: procID= -grpFunc
grpFunc > 0 => signal is sent only to the process where: procID=grpFunc
sigGrp is true at return if procID=grpFunc was found, false otherwise;
å
type action = (sigGoOn, sig, GoOn);
VAR res : ResultType; ac : action;
BEGIN
SCheck( grp^^.grpGate.Lock); æcannot speed up, propMode = stop !! å
res:= Call( FirstInSet(grp^^.procSet,proc));
sigGrp:= false;
WHILE res.main = ok DO BEGIN
WITH p = proc^^.pData^^ DO BEGIN
IF grpFunc = 0 THEN ac:=sigGoOn ELSE
IF (grpFunc < 0) AND (p.procID <> -grpFunc) THEN ac:=sigGoOn ELSE
IF (grpFunc > 0) AND (p.procID = grpFunc) THEN ac:=sig ELSE ac:=GoOn;
IF (ac=sigGoOn) or (ac=sig) THEN BEGIN æ do send signal to process å
noOfSignals := noOfSignals + 1; æset to zero by callerå
sigProc(proc,p,grp^^.dir,sigNo);
IF ac = sig THEN BEGIN sigGrp:=true; res.main:= ok+1; END;
END æ sig å ;
IF (ac=sigGoOn) or (ac=GoOn) THEN BEGIN
res:= Call( NextInSet(grp^^.procSet,proc));
END æ GoOn å;
END æWITH På;
END æWHILEå;
SCheck( grp^^.grpGate.Open);
END æsigGrpå;
æ$Eå
PROGRAM UNIPSGRP OBJECT GROUPOBJ WITH grpEnv ; æ******************å
æ*** LOCAL PROCEDURES IN GROUP OBJECT ***å
FUNCTION NEWUPROC ( att : Boolean;
VAR parent : PprocEnv;
VAR proc : PprocEnv; æ return value å
VAR tempOwn : kpt ; æ return value å
VAR divPD : divPDType) : ResultType;
æ divPD recieves process values from UniData ( and grpData ? ) å
VAR res : ResultType; i : Long; newPsize : sizeType;
BEGIN æ propagate(rej) is set by caller å
res.main:= ok;
IN æ allocation may fail å
æ get data values for the new proc, at present all values can
be found in the unix object å
XCheck( unix^^.unigate.Lock); æ may speed up å
WITH u = unix^^.uData^^ DO BEGIN
divPD.procID:= u.nxtProcID;
u.nxtProcID:= u.nxtProcID + 1;
divPD.noOfFiles:= noOfFiles; æ CONST at present å
divPD.fenvSize:= u.fenvSize;
divPD.extSize:= u.extSize;
IF att THEN newPsize:= u.extSize ELSE newPsize:= u.procSize;
END æWITH uå;
SCheck( unix^^.uniGate.Open);
IF res.main <> ok THEN exception(res);
XCheck( unix^^.sched.NewProc(OUT tempOwn; newPsize)); æsize fixed at INITå
XCheck( DeclEnv(tempOwn, tmpManSet, proc, refs(procEnv),
0, makeSize(0,0), makeSize(-1,-1))); æ process envelope å
SCheck( Copy(tempOwn, proc^^.egoObj));
SCheck( Copy(proc, proc^^.egoEnv));
SCheck( Copy(egoEnv, proc^^.procGrp));
SCheck( Copy(parent, proc^^.parent));
XCheck( unix^^.alloc.newObj(OUT proc^^.fileOwn; divPD.fenvSize, OUT i));
SCheck( DeclEnv(proc^^.fileOwn, fenvSet, proc^^.files, divPD.noOfFiles,
0, makeSize(0,0), makeSize(-1,-1))); æ file envelope å
XCheck( newSeg(proc^^.pData, bytes(procData)));
XCheck( grpGate.NewCond(OUT proc^^.waitCond));
XCheck( grpGate.NewCond(OUT proc^^.zombCond));
DO IF res.main = ok THEN res:= getException;
æ in case of errors created objects will be deallocated by the caller å
newUproc:= res;
END ænewUprocå;
ENTRY GETPARAMS æRESERVEDå
WITH RECORD t:^^; END;
BEGIN ObjReturn(univRes(EntryIllegal)) END;
æ$Eå
ENTRY PREPEXEC æOUT execArgs : Ptext;
IN sourceName : text;
IN argBytes : Long å
æ Called by unix system call 'exec'.
load program, create callable application object and maybe a new
extension object. If everything succeeds create and return a segment
object in execArgs å
WITH RECORD t:^^;
proc : PprocEnv;
newExt : ^^;
cur : PprocEnv; æ working pointer for 'regret alarm' code å
END;
VAR res : ResultType; i : Long; segSize : sizeType;
first,last : integer;
BEGIN
æ#ENTRY# PS('PREPEXEC') &ENTRY&å ;
RCheck( inspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
SCheck( propagate(rej)); res.main:= ok;
p.execLoad:= WName('L', p.procID, p.progCount);
p.execCrea:= p.execLoad; p.execCreaÆ1Å := 'C' ;
æ If the sourceName specifies the default file system named '$'
this file system may be skipped by dir ??å
last:= elements(sourceName);
first:= 1;
IF last > 2 THEN BEGIN
IF ( sourceNameÆ1Å='$' ) AND ( sourceNameÆ2Å='/' ) THEN first:= 3;
END ;
RCheck( dir.Load(;FIX(p.execLoad), sourceNameÆfirst..lastÅ));
IN æ program loaded å
XCheck( dir.Create(OUT proc^^.execProg; FIX(p.execCrea), p.execLoad));
IN æ program created å
IF argBytes > 0 THEN BEGIN
SCheck( ClearSize(segSize));
SCheck( AddSeg(segSize,argBytes));
XCheck( unix^^.alloc.NewObj(OUT execArgs; segSize, OUT i));
XCheck( DeclSeg(execArgs, argBytes));
END ;
(* NOT implemented YET :
IF "callstack req. of created program > p.execExt" THEN BEGIN
XCheck( unix^^.alloc.NewObj(OUT newExt; "NEWSIZE", OUT i));
SCheck( Copy(newExt, proc^^.execExt));
SCheck( MoveOwn(newExt, execExtO));
p.useNewExt:= true;
END ;
*)
æ PREPEXEC has succeded. The child is allowed to copy data into the
execArg segment and return to UNIPS. All calls to UNIPS will be
rejected and signals cannot be caught å
SCheck( propagate(stop)); æa pending signal must not speed up the lockå
SCheck( grpGate.Lock);
p.procState:= exec;
p.valBytes:= argBytes;
p.progCount:= p.progCount + 1;
FOR i:= 1 TO noOfSignals DO BEGIN
IF p.sigÆiÅ.sigState <> ignore THEN BEGIN
p.sigÆiÅ.sigState:= default;
IF p.sigÆiÅ.sigPend THEN BEGIN æ new prog killed during PREPEXEC å
p.termStatus := i; æ means process killed å
NoCheck( dir.AbortObj(; p.callCrea));
END ;
END ;
END æFORå;
SCheck( grpGate.Open);
æ note that the process is not removed from the alarmSet å
DO BEGIN
IF res.main = ok THEN res:= getException;
NoCheck( Dealloc(execArgs, execArgs)); æmay not have been created yetå
SCheck( dir.deleteItem(; p.execCrea));
exception(res);
END ;
DO BEGIN
IF res.main = ok THEN res:= getException;
SCheck( dir.deleteItem(; p.execLoad));
END ;
ObjReturn(res);
END æWITH på;
END æENTRY PREPEXECå;
PRIVATE FORKPROC (IN proc : PprocEnv ;
IN parent : PprocEnv);
æ$Eå
ENTRY FORKBEGIN æIN stack : kpt ; <== should be a formal subsegment!!!!
IN data : kpt å
æ Because stack and data segment of the calling unips utility are
parameters to the entry, value parameters cannot easily be added
to the parameter list. However, childID of the new process must be returned
to the calling process. The value could be a VAR parameter (subsegment)
of an other temp data segment created by the run time system,
but at present childID is returned via the standard result value !!!!!! å
æ !!!!! if stack IS a formal subsegment, childId could be normal value data å
æ Create a new process, and let the NEW process turn itself into a copy
of the calling process. The entry is called by the unix system call 'fork' å
WITH RECORD t:^^;
proc : PprocEnv; æcalling procå
newChild: PprocEnv;
tempOwn : ^^ ; æ owns process object å
tempExt : ^^ ; æ owns extension object å
END ;
VAR res : ResultType; i : Long; divPD : divPDType;
BEGIN
æ#ENTRY# PS('FORKBEGIN') &ENTRY&å ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
SCheck( Propagate(rej)); res.main:= ok;
RCheck( unix^^.alloc.NewObj(OUT tempExt; p.extSize, OUT i));
RCheck( newUproc(false,proc,newChild,tempOwn,divPD));
SCheck( Copy(tempExt,newChild^^.execExt));æ childs ref to extObj å
WITH c = newChild^^.pData^^ DO BEGIN
c.procID:= divPD.procID;
c.parentID:= p.procID;
c.callLoad:= p.callLoad; æ child runs the same program as parent å
c.progCount:= 0; æ index of a forked program å
c.callCrea:= WName('C',c.procID,c.progCount);
c.progCount:= 1; æ index of first execed program å
c.noOfChildren:= 0;
c.termStatus:= 0;
c.procState:= p.childState; æ child or embryo å
c.childState:= embryo;
c.useNewExt:= false; æ the first extension object is of cause used å
c.alarmTime:= 0;
c.valBytes:= p.valBytes; æ size of original value data å
c.extSize:= divPD.extSize;
FOR i:= 1 TO noOfSignals DO BEGIN
c.sigÆiÅ.sigState:= p.sigÆiÅ.sigState;
c.sigÆiÅ.sigPend:= false;
END æFORå;
RCheck( CallExtend(tempExt,ClSh,CloneFileEnv,
proc^^.files,newChild^^.files));
RCheck( CallExtend(tempExt,dir,create,
OUT newChild^^.callProg; FIX(c.callCrea), c.callLoad));
RCheck( grpGate.Lock); æ may speed up, ==> child will be deallocated å
æ from this point nothing can prevent declaration of the child process å
æ however the statements below could be rearranged so that errors from å
æ MoveOwn ( should not occur ) can be handled correctly (XCheck) å
SCheck( MoveMan(newChild,procSet)); æ makes child visible å
SCheck( MoveOwn(tempExt,execExtO)); æ must not fail å
SCheck( Copy(stack,proc^^.forkStack));
SCheck( Copy(data,proc^^.forkData));
SCheck( DeclProc(tempOwn,forkProc,newChild,proc));
SCheck( MoveOwn(tempOwn,procOwn)); æ must not fail å
p.exitRes.main:= ok; æ may be changed by child if it fails å
æ c.procState equals embryo, except when attProc calls FORKBEGIN å
IF c.procState = embryo THEN BEGIN æ wait for forkEnd å
res:= Call( proc^^.waitCond.wait); æ may be speeded up or signaled å
IF res.main = -speededUp THEN BEGIN æ reject the fork å
p.exitRes:= res;
c.procState:= aborted;
NoCheck( dir.AbortObj(; c.callCrea));
æ the child process may execute in the initial context (FORKPROC) å
æ**** NoCheck( SpeedUpProc(void,newChild)); NOT IMPLEMENTED YET å
END æspeedupå;
END æwaitå;
IF c.procState = child THEN BEGIN
p.noOfChildren:= p.noOfChildren + 1;
p.exitRes.orgNo:= c.procID MOD (SHIFT16);
p.exitRes.orgSys:= c.procID DIV (SHIFT16);
END ;
SCheck( Copy(void,proc^^.forkStack)); æ must be done å
SCheck( Copy(void,proc^^.forkData)); æ must be done å
SCheck( grpGate.Open);
ObjReturn(p.exitRes);
END æWITH cå;
END æWITH på;
END æENTRY FORKBEGINå;
æ$Eå
ENTRY FORKEND
æ Called by the embyo when the environment of the parent has been copied å
WITH RECORD t:^^;
proc : PprocEnv;
papa : PprocEnv;
END ;
VAR res : ResultType; i : Long;
BEGIN
æ#ENTRY# PS('FORKEND') &ENTRY&å ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> embryo THEN ObjReturn(univRes(ObjectState));
SCheck( Propagate(rej)); res.main:= ok;
RCheck( grpGate.Lock); æ can only be speeded up by abortion because å
æ no signals are caught by embryo å
æ since the child reaced so far, the parent is still waiting with exitRes=ok å
p.procState:= child;
SCheck( Copy(proc^^.parent,papa));
SCheck( papa^^.waitCond.Signal);
SCheck( grpGate.Open);
ObjReturn(res);
END æWITH på;
END æENTRY FORKENDå;
æ$Eå
PRIVATE CLEANUP (IN proc : PprocEnv)
æ Dealloc objects owned by the process envelope AND remove proc from alarmSet.å
æ CleanUp is called to utilize the free stack of an extension object during å
æ dealloc. GrpGate must not be open while uniGate is locked by cleanUp.å
WITH RECORD t:^^;
END ;
VAR rep : boolean;
BEGIN
æ#ENTRY# PS('CLEANUP') &ENTRY&å ;
NoCheck( Dealloc(proc^^.fileOwn,proc^^.fileOwn));
NoCheck( Dealloc(proc^^.zombCond,proc^^.zombCond)); æ release zombies å
NoCheck( Dealloc(proc^^.waitCond,proc^^.waitCond));
WITH p = proc^^.pData^^ DO BEGIN
rep:= true;
WHILE rep DO BEGIN æ only two loops are executed å
SCheck( unix^^.uniGate.Lock);
IF p.alarmTime = 0 THEN rep := false
ELSE BEGIN
p.alarmTime:= Ragnarok; æ proc should be removed å
æ u.newTime is zero, alarmProc will remove proc from alarmSet å
SCheck( unix^^.alarmCond.Signal);
æ p.alarmTime is set to zero by alarmProc å
END ;
SCheck( unix^^.uniGate.Open); æ allow alarmProc to run å
END æWHILEå;
END æWITH pø;
END æPRIVATE CLEANUPå;
æ$Eå
PRIVATE FORKPROC æIN proc : PprocEnv ;
IN parent : PprocEnv å
æ Unix processes executing unix utility programs starts in this entry å
WITH RECORD t:^^;
callArgs : ^^ ; æ segment object å
execArgs : ^^ ; æ segment object å
callExt : ^^ ; æ extension object å
kid : PprocEnv;
END ;
VAR res : ResultType; i : Long; remove, progUsed : boolean; LABEL 0;
BEGIN
æ#ENTRY# PS('FORKPROC') &ENTRY&å ;
SCheck( Propagate(rej));
WITH p = proc^^.pData^^ DO BEGIN
IN æ This IN block should be reprogrammed using IF !!??å
SCheck( MoveOwn(proc^^.execExt,callExt)); æ extObj created by parent å
æ next two alloc calls has been moved to FORKBEGIN. å
æ XCheck( CallExtend(callExt,ClSh,CloneFileEnv, å
æ parent^^.files,proc^^.files)); å
æ XCheck( CallExtend(callExt,dir,create, å
æ OUT proc^^.callProg; FIX(p.callCrea), p.callLoad));å
IF p.procState = child THEN BEGIN æ initial fork to login program å
p.valBytes:= 70; æ *********** obs: must match the TOTAL below å
p.exitRes:= Call( CallExtend( callExt, proc^^.callProg, run,
æunips standard:å proc^^.files, egoObj, OUT execArgs;
æ 6+2 byteså 'llgin',
æ 0+2 byteså ''æ end of arguments å ,
æ 6+2 byteså 'HOME=/',
æ20+2 byteså 'PATH=:/bin:/usr/bin',
æ 0+2 byteså ''æ end of environment å ,
æ10+2 byteså '$/unixfs/' æ ClSh + lowlevel FS å ,
æ10+2 byteså '$/pipefs/' æ ClSh + pipe FS å ,
æ 2+2 byteså '/' æ current working dir å));
æTOTAL= 54+16 =70, must have been assigned to p.valBytes above å
END ELSE
IF p.procState = embryo THEN BEGIN æ parent is still waiting å
p.exitRes:= Call( ObjCallExtend( callExt, proc^^.callProg, fork,
ædummy values:å t, p.valBytes,
æunips standard:å proc^^.files, egoObj, æOUTå execArgs,
æoptionals:å parent^^.forkStack, parent^^.forkData));
END ;
IF p.procState = embryo THEN exception(p.exitRes);
DO BEGIN æ the fork has failed å
SCheck( Propagate(stop));
SCheck( grpGate.Lock);
IF p.procState = embryo THEN BEGIN æ parent is waiting å
WITH dad = parent^^.pData^^ DO BEGIN
dad.exitRes:= getException;
IF dad.exitRes.main = ok æ ok may have been returned from appl å
THEN dad.exitRes:= univRes(giveUp);
SCheck( parent^^.waitCond.Signal);
END æWITH dadå;
END æ waiting parent å;
SCheck( grpGate.Open);
SCheck( Propagate(rej)); æ not realy needed å
END æfork has failedå;
REPEAT æ exec loop å
NoCheck( CallExtend(callExt,dir,deleteItem ;p.callCrea));
æ check whether "children" or "parents" are using the loaded prog å
res:= Call( FirstInSet(procSet,kid));
REPEAT æ always at least one proc in set å
progUsed:=false;
WITH cp=kid^^.pData^^ DO BEGIN
IF p.procID <> cp.procID THEN BEGIN æ other process å
progUsed:=true;
FOR i:=bytes(dirWName) DOWNTO 1 DO
IF p.callLoadÆiÅ <> cp.callLoadÆiÅ THEN progUsed:=false;
END æ other process å;
END æWITHå;
IF NOT progUsed THEN res:= Call( NextInSet(procSet,kid));
UNTIL progUsed OR (res.main <> ok);
IF NOT progUsed THEN
NoCheck( CallExtend(callExt,dir,deleteItem ;p.callLoad));
NoCheck( Dealloc(callArgs,callArgs));
IF p.procState <> exec THEN goto 0; æ exit the exec repeat loop å
p.procState:= child;
æ has a new extension object been defined in PREPEXEC ?å
IF p.useNewExt THEN BEGIN æ a new extension object has been created å
SCheck( MoveOwn(proc^^.execExt,callExt));
SCheck( FirstInSet(callExt,kid)); æ kid:= old extension å
SCheck( Dealloc(callExt,kid));
p.useNewExt:= false;
END ;
SCheck( grpGate.Lock); æ speedUp do not occur å
p.callLoad:= p.execLoad;
p.callCrea:= p.execCrea;
SCheck( Copy(proc^^.execProg,proc^^.callProg));
SCheck( MoveOwn(execArgs,callArgs));
i:= p.termStatus;
Scheck( grpGate.Open);
IF i = 0 THEN BEGIN æ proc has not been killed å
p.exitRes:= Call( ObjCallExtend( callExt, proc^^.callProg, run,
ævalueData:å callArgs, p.valBytes,
æunips standard:å proc^^.files, egoObj, æOUTå execArgs,
ædummy optionals:å t,t));
END ;
UNTIL false ;
0: NoCheck( CallExtend(callExt,void,cleanUp,IN proc));
æ cleanUp will deallocate the fileEnv and the condition objects in the
process envelope. It also removes the process from the alarmSet å
NoCheck( Dealloc(callExt,callExt));
æ execArgs may own an object returned by the last clumsy program å
IF p.procState <> child THEN BEGIN
P.procState := dead ;
æ at present dead processes are deallocated "by luck" (see below
and in attProc. ADAM may be implemented later å
END ELSA BEGIN
æ the terminating process did become a child; it may have:
1) zombies : turned into dead processes
2) children: loose their parent
3) parent : activated if waiting for children to terminate
å
Scheck( grpGate.Lock);
IF p.noOfChildren > 0 THEN BEGIN
res:= Call( FirstInSet(procSet,kid));
WHILE res.main = ok DO BEGIN
WITH c = kid^^.pData^^ DO BEGIN
IF c.parentID = p.procID THEN BEGIN æ child is found å
IF c.procState = zombie
THEN c.procState:= dead
ELSE c.parentID := 1 æno parentå ;
END ;
IF c.procState = dead THEN remove:= true ELSE remove:= false;
END æWITH cå;
IF remove THEN SCheck( Copy(kid^^.egoObj,callExt)); æsave refDeadå
res:= Call( NextInSet(procSet,kid));
IF remove THEN SCheck( Dealloc(procOwn,callExt));
END æWHILEå;
END ænoOfChildren > 0å;
res.main:= ok; æprepare parent handlingå
IF p.parentID = 1 THEN BEGIN æ no parent å
p.procState:= dead;
END ELSE BEGIN
p.procState:= zombie;
IN æ zombCond may be deallocated by a terminating parent å
WITH papa = parent^^.pData^^ DO BEGIN
IF papa.procState <> waiting THEN BEGIN
XCheck( parent^^.zombCond.Wait);
æ exception with res.main=status when cond is deallocated å
END ;
papa.zombieID:= p.procID;
æ note the signbit problem in the next statement å
papa.zombieStatus:= (p.exitRes.auxCause * 256) + p.termStatus;
SCheck( Copy(proc,deadZombie)); æ zombie is a shared pointer:OK!å
SCheck( parent^^.waitCond.Signal);
END æWITH papaå;
DO res:= getException;
END æparent handlingå;
IF res.main <= ok æ reject or ok å
THEN SCheck( grpGate.Open);
END æchild terminationå;
ObjReturn(res); æ return from initial context å
END æWITH på;
END æPRIVATE FORKPROCå;
æ$Eå
ENTRY WAITexit æOUT procID : Long ;
OUT status : Long å
æ Called by unix system call 'wait' å
WITH RECORD t:^^;
proc : PprocEnv;
END ;
VAR res : ResultType; i : Long;
BEGIN
æ#ENTRY# PS('WAIT') &ENTRY&å ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
SCheck( Propagate(rej)); res.main := ok;
RCheck( grpGate.Lock); æ may speed up å
p.procState:= waiting;
IN æ gate locked å
IF p.noOfChildren = 0 THEN BEGIN
procID:= -1;
exception(res); æ res=okå
END ;
SCheck( proc^^.zombCond.Signal); æif zombies are queued, activate firstå
XCheck( proc^^.waitCond.Wait); æ may speed up å
æ zombie signal recieved å
status:= p.zombieStatus;æ assigned by zombie å
procID:= p.zombieID; æ assigned by zombie å
NoCheck( Dealloc(procOwn,deadZombie^^.egoObj));
p.noOfChildren:= p.noOfChildren - 1;
DO IF res.main = ok THEN res:= getException;
p.procState:= child;
SCheck( grpGate.Open);
ObjReturn(res);
END æWITH på;
END æENTRY WAITå;
æ$Eå
ENTRY PAUSE
æ Called by unix system call 'pause' å
WITH RECORD t:^^;
proc : PprocEnv;
END ;
VAR res : ResultType; i : Long;
BEGIN
æ#ENTRY# PS('PAUSE') &ENTRY&å ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
SCheck( Propagate(rej));
RCheck( grpGate.Lock); æ may speed up å
res:= Call( proc^^.waitCond.Wait); æ waitState = noWait å
æ waitCond will never be signaled, but will be speeded up å
SCheck( grpGate.Open);
ObjReturn(res);
END æWITH på;
END æENTRY PAUSEå;
æ$Eå
ENTRY ALARM æIN secs : Longå
æ Called by unix system call 'alarm' å
WITH RECORD t:^^;
proc : PprocEnv;
END ;
VAR res : ResultType; i : Long;
BEGIN
æ#ENTRY# PS('ALARM') &ENTRY&å ;
IF secs <= 0 THEN secs := 0 æ regret alarm å ELSE BEGIN
res:= Call( unix^^.uniClock.GetClock(; OUT i )); æ i:= NOW å
IF res.main <> ok THEN i:= 1; æ dummy value when no clock is present å
IF Ragnarok - i < secs THEN secs := Ragnarok -1 ELSE secs := secs + i ;
END ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectStatus));
SCheck( Propagate(rej)); res.main:= ok;
RCheck( unix^^.uniGate.lock); æ may speed up å
WITH u = unix^^.uData^^ DO BEGIN
IF secs = 0 THEN BEGIN æ proc should not be in alarmSet å
IF p.alarmTime <> 0 æ proc IS in alarmSet å
THEN p.alarmTime := Ragnarok; æ proc will be removed from set å
END ELSE BEGIN æ proc should belong to the alarmSet å
IF p.alarmTime = 0 THEN BEGIN æ proc is NOT in set å
æ move proc into set å
IF u.noOfAlarms > 0 æ set is not empty å
THEN SCheck( Copy(unix^^.alarmSet,proc^^.alarmSet));
SCheck( Copy(proc,unix^^.alarmSet));
u.noOfAlarms := u.noOfAlarms + 1 ;
END ;
p.alarmTime := secs; æ proc is now ok in set å
IF secs < u.alarmTime THEN BEGIN
æ new proc is the next proc to recieve alarm signal å
u.newTime:= secs;
SCheck( unix^^.alarmCond.Signal); æ notify alarmProc å
END ;
END ;
END æWITH uå;
SCheck( unix^^.uniGate.Open);
ObjReturn(res);
END æWITH på;
END æENTRY ALARMå;
æ$Eå
ENTRY SIGSAG æ IN OUT sigOp : Long ;
IN OUT sigNo : Long ;
IN OUT sigFunc : Long å
æ Called by signal handling parts of the unix system call library
sigOp=1 : getSigState(IN sigNo ; OUT sigFunc);
sigOp=2 : setSigState(IN signo ; IN OUT sigFunc); =signal
sigOp=3 : recieveSig (OUT noOfPending; OUT sigNo; OUT sigFunc);
sigOp=4 : sendSignal (OUT noOfSignaled; IN sigNo; IN procID ); =kill
sigOp=5 : egoSignal (OUT noOfSignaled; IN sigNo; IN dummy );
å
WITH RECORD t:^^;
proc : PprocEnv;
work : PprocEnv;
grp : PgrpEnv ;
END ;
VAR res : ResultType; i : Long; found : boolean;
BEGIN
æ#ENTRY# PSS('SIGSAG',sigOp) &ENTRY&å ;
IF (sigOp>0)AND(sigOp<6)AND(sigNo>0)AND(sigNo<=noOfSignals) THEN æOKå
ELSE ObjReturn(univRes(DataValue));
RCheck(InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
res.main:= ok;
IF sigOp < 4 THEN BEGIN
SCheck( Propagate(rej));
RCheck(grpGate.Lock); æ may speed up å
IF sigOp = 3 THEN BEGIN æ recieve signal å
sigOp:= 0; æ noOfPending := 0 å
FOR i:= 1 TO noOfSignals DO BEGIN
IF p.sigÆiÅ.sigPend THEN BEGIN
sigOp:= sigOp + 1; æ increase noOfPending å
sigFunc:= p.sigÆiÅ.sigState;
sigNo:= i;
END ;
END æFORå;
IF sigOp <> 0 THEN BEGIN æ signal has been caught å
p.sigÆsigNoÅ.sigPend:= false;
IF ( sigNo <> SIGILL ) AND ( sigNo <> SIGTRAP ) THEN BEGIN
p.sigÆsigNoÅ.sigState := default ;
END ;
END ;
END æ recieve signal å
æ$Eå
ELSE BEGIN æ set/get sigState å
WITH item = p.sigÆsigNoÅ DO BEGIN
i:= item.sigState;
IF sigOp=2 THEN BEGIN æsetSigStateå
IF sigNo <> SIGKILL THEN BEGIN æ kill signal cannot be redefined å
item.sigState:= sigFunc;
item.sigPend:= false; æ normally it will be false already å
END ;
END ;
sigFunc:= i;
END æWITH itemå;
END æ set/get å;
SCheck( grpGate.open);
ObjReturn(res);
END æ sigOp = 1 , 2 or 3 å;
IF sigOp=5 THEN sigFunc:= p.procID; æ kill ego å
IF sigFunc = -1 THEN sigFunc:= -p.procID; æ kill all except ego å
IF sigFunc = p.procID THEN BEGIN æ kill ego, sigOp may be 4 or 5 å
SCheck( Propagate(rej));
RCheck( grpGate.lock); æ may speed up å
sigProc(proc,p,dir,sigNo);
SCheck( grpGate.open);
objReturn(res);
END;
END æWITH på; æ rather peculier nesting of blocks å
æ sigOp = 4 : send signals : kill å
sigOp:= 0; æ noOfSignaled:= 0; å
IF sigFunc = 0 THEN BEGIN æ kill all procs in own group å
found:= sigGrp(egoEnv,work,sigOp,sigNo,sigFunc); æ no speed up å
END ELSE BEGIN æ scan group set å
SCheck( unix^^.uniGate.Lock); æ prevent group dealloc. no speed up å
SCheck( FirstInSet(unix^^.grpSet,grp));
REPEAT æ always at least one group å
found:=sigGrp(grp,work,sigOp,sigNo,sigFunc);
IF NOT found THEN res:= Call( NextInSet(unix^^.grpSet,grp));
UNTIL ( res.main <> ok ) OR found ;
SCheck( unix^^.uniGate.Open);
END æ group scan needed å;
res.main:= ok;
ObjReturn(res); æ sigOp equals noOfSignaled processes å
END æENTRY SIGSAGå;
æ$Eå
ENTRY SETGETID æIN IDID : Long ;
IN OUT ID : IDType å
æ Called by ID handling parts of the system call library
IDID=1 : getPid; IDID=2 : getPPid; IDID=3 : getPGrp
IDID=4 : getUid; IDID=5 : getGid; IDID=6 : getEUid; IDID=7 : getEGid
IDID=8 : setPGrp; IDID=9 : setUid; IDID=10: setGid;
****** at present only getPid is implemented
å
WITH RECORD t:^^;
proc : PprocEnv;
END ;
VAR res : ResultType; i : Long;
BEGIN
æ#ENTRY# PSS('SETGETID',IDID) &ENTRY&å ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
res.main:= ok;
IF IDID <> 1 THEN ID:= 0 ELSE ID := p.procID;
ObjReturn(res);
END æWITH på;
END æENTRY SETGETIDå;
æ$Eå
ENTRY GetUniqueId æ ; OUT uniqueId : fullId å
WITH RECORD t : ^^;
proc : PprocEnv;
END;
VAR
res : resultType;
i : integer;
id : idType;
æ This entry creates a unique pipe id as a text string of the form
'p:<PipeId>' terminated by a null char. The string is 13 chars long
å
BEGIN
æ#ENTRY# PS('GETUNIQUEID ') &ENTRY&å ;
RCheck( InspProc(egoEnv,proc,i));
WITH p = proc^^.pData^^ DO BEGIN
IF p.procState <> child THEN ObjReturn(univRes(ObjectState));
IF 13 > elements(uniqueId) THEN ObjReturn(univRes(DataValue));
res.main:= ok;
XCheck( unix^^.uniGate.Lock ); æmay speed upå
WITH u = unix^^.uData^^ DO
BEGIN
id := u.nxtPipeID;
u.nxtPipeID := u.nxtPipeID + 1;
END; æwith uå
SCheck( unix^^.uniGate.Open );
æconvert id to text stringå
FOR i := 12 DOWNTO 3 DO
BEGIN
uniqueIdÆiÅ := chr( ord('0') + (id MOD 10));
id := id DIV 10;
END;
uniqueIdÆ1Å := 'p';
uniqueIdÆ2Å := ':';
uniqueIdÆ13Å:= chr(0);
ObjReturn(res);
END æWITH på;
END; æENTRY getUniqueIdå
æ$Eå
PRIVATE ATTPROC (IN proc : PprocEnv;
IN termName : text ;
IN loginProg : text )
æ The attention process forks to the login program when the control
terminal becomes ready. The attention process sends 'interrupt' and
'hangUp' signals to processes in the group. The attention process
will fork to the login program once more in case it discovers
that all the processes of the group has terminated
å
WITH RECORD t:^^;
files : PfileEnv;
sssProc : PprocEnv;
work : PprocEnv;
END ;
VAR res : ResultType; i : Long; sig : Long; LProcID : IDType;
restart, remove, reload, ready : Boolean;
BNL : ARRAY Æ1..3Å OF char; æ BIG New Line å
BEGIN
æ#ENTRY# PS('ATTPROC') &ENTRY&å ;
BNLÆ1Å:= chr(13);
BNLÆ2Å:= chr(10);
BNLÆ3Å:= chr(13);
SCheck( Copy(proc^^.files,files));
WITH p = proc^^.pData^^ DO BEGIN
æ at entry the loginProg has already been loaded å
reload:= false;
REPEAT æ login progcess is not running å
REPEAT æ control terminal is not ready å
ready:= false;
IN æ terminal may stil not be ready OUT is required by windowfs å
XCheck(files^^Æ2Å.WriteSeq(VAR IN OUT BNL; OUT i, IN uxio));
XCheck(files^^Æ2Å.WriteSeq(VAR IN OUT termName; OUT i, IN uxio));
XCheck(files^^Æ2Å.WriteSeq(VAR IN OUT BNL; OUT i, IN uxio));
XCheck(files^^Æ2Å.WriteSeq(VAR IN OUT loginProg; OUT i, IN uxio));
XCheck(files^^Æ2Å.WriteSeq(VAR IN OUT BNL; OUT i, IN uxio));
ready := true;
DO BEGIN
æ if the calls below returns "not ok" immediatly
because ClSh or files^^Æ2Å is in error, the attProc
will loop endlessly and waste CPU time å
NoCheck( ClSh.waitSignal(; OUT i)); æ calls waitBreak å
NoCheck( files^^Æ2Å.WaitReady);
END ;
UNTIL ready;
IF reload THEN NoCheck( dir.Load(; IN FIX(p.callLoad), IN loginProg));
reload:= true;
res:= Call( egoObj.forkBegin(void,void));
IF res.Main <> ok THEN restart:= true ELSE BEGIN
restart:= false;
LProcID:= (res.orgSys * shift16) + res.orgNo;
END ;
WHILE NOT restart DO BEGIN
æ take care of signals generated by the control terminal å
res:= Call( ClSh.waitSignal(; OUT i)); æ calls waitBreak å
IF res.main = ok
THEN sig := SIGINT
ELSE sig := SIGHUP ;
SCheck( grpGate.Lock);
res:= Call( FirstInSet(procSet,sssProc));
restart:= true;
WHILE res.main = ok DO BEGIN
WITH sp = sssProc^^.pData^^ DO BEGIN
IF sp.procState = dead THEN remove:= true ELSE BEGIN
sigProc(sssProc,sp,dir,sig);
remove:= false;
restart:= false; æ because processes are still executing å
END ;
END æWITH spå;
IF remove THEN SCheck( Copy(sssProc,work));
res:= Call( NextInSet(procSet,sssProc));
IF remove THEN NoCheck( Dealloc(procOwn,work^^.egoObj));
END æWHILEå;
SCheck( grpGate.Open);
NoCheck( files^^Æ2Å.WaitReady); æ remove break status å
END æWHILEå;
UNTIL false;
END æWITH på;
END æPRIVATE ATTPROCå;
æ$Eå
PRIVATE STARTGRP æIN ShareMaster : refShClFS;
IN termName : text ;
IN loginProg : text å
æ Create attention process , ClSh and dir å
WITH RECORD t:^^;
files : PfileEnv;
proc : PprocEnv;
NilDad: PprocEnv;
END ;
VAR res : ResultType; i : Long; divPD : divPDType;
BEGIN
æ#ENTRY# PS('STARTGRP') &ENTRY&å ;
RCheck( newUproc(true,NilDad,proc,procOwn,divPD)); æ create attProc å
æ proc stays in tmpManSet as the first member å
WITH p = proc^^.pData^^ DO BEGIN
p.procID:= 1; æ children of this proc has "no parent" å
p.parentID:= divPD.procID; æ could serve as group identifier å
p.progCount:= 0;
p.callLoad:= WName('L',divPD.procID,p.progCount);
p.noOfChildren:= 0; æ counts #times loginProcess has been created å
p.procState:= child;
p.childState:= child; æ special value in attProc å
p.alarmTime:= 0; æ not used å
p.termStatus:= 0; æ not used å
p.extSize:= divPD.extSize; æ used by forkbegin å
FOR i:= 1 TO noOfSignals DO BEGIN
p.sigÆiÅ.sigState:= default; æ copied to login process å
p.sigÆiÅ.sigPend:= false;
END æFORå;
æ***SCheck( Copy(unix^^.dir,dir)); used in tests without subdir ***å
RCheck( unix^^.dir.NewSubDir(OUT dir; IN FIX(WName('S',divPD.procID,0))));
RCheck( ShareMaster.createGroup(OUT ClSh, IN dir; IN termName));
RCheck( dir.InsertPointer(IN ClSh; IN '$'));
RCheck( dir.Load(; IN FIX(p.callLoad), IN loginProg));
SCheck( Copy(proc^^.files,files));
FOR i:= 1 TO 3
DO RCheck( ClSh.Assign(OUT files^^ÆiÅ; IN '$//dev/tty', IN readwrite));
SCheck( MakeReentrant(void));
RCheck( DeclProc(procOwn,attProc,tmpManSet;termName,loginProg));
res.main:= ok;
ObjReturn(res);
END æWITHå;
END æPRIVATE GRPINITå;
æ$Eå
OTHERWISE GRPother
WITH RECORD t:^^;
END;
BEGIN
æ#ENTRY# PS('GRPother') &ENTRY&å ;
ObjReturn(univRes(EntryIllegal));
END ;
END æ PROGRAM UNIPSGRPå ;
PROGRAM UNIPSimpl OBJECT UNIPS WITH uniEnv ; æ*****************å
æ*********************************************************************å
PROCEDURE ABORTGRP (VAR tempGrp : PgrpEnv ; æ temp pointer å
VAR work : PprocEnv);
æ UniGate must be locked by caller, and propMode must be set to stop å
VAR res : ResultType; i : Long; b : Boolean;
BEGIN
SCheck( MoveMan(tempGrp,tempGrp)); æ remove group from group set å
b:= sigGrp(tempGrp,work,i,SIGKILL,0); æ kill all procs in group å
SCheck( Abort(tempGrp)); æ speeds up and terminates attProcess å
res:= Call( FirstInSet(tempGrp^^.procSet,work));
WHILE res.main = ok DO BEGIN
WITH p = work^^.pData^^ DO p.alarmTime:= Ragnarok; æ remove proc å
res:= Call( NextInSet(tempGrp^^.procSet,work));
END æWHILEå;
SCheck( alarmCond.Signal); æ activate alarm proc å
SCheck( unigate.Open); æ wait until all å
SCheck( unigate.Lock); æ procs are removed å
END æabortGrpå ;
æ$Eå
PRIVATE ALARMPROC
æ The alarm process generates alarm signals.
At present the alarm process is NOT used as ADAM.
At present the alarmSet is NOT sorted ! (performancer)
å
WITH RECORD t:^^;
proc : PprocEnv;
prvProc : PprocEnv;
group : PgrpEnv;
END ;
VAR res : ResultType; NOW : Long; qix : Integer ; remove : Boolean;
TimePar : RECORD zero : Long; usec : Long; END ;
BEGIN
æ#ENTRY# PS('ALARMPROC ') &ENTRY&å ;
NoCheck( dir.getRef(OUT uniClock; IN 'clock'));
æ when no clock is present, alarms are not generated correctly å
SCheck( uniGate.Lock);
WITH u = uData^^ DO BEGIN
u.noOfAlarms:= 0; æ no of procs in alarmSet å
TimePar.zero:= 0; æ never changed å
TimePar.usec:= 0; æ no time Out at all å
u.alarmTime := Ragnarok; æ next time alarmSet is searched å
u.newTime:= 0; æ assigned by ENTRY ALARM å
res:= Call( dir.speedUpObj(; ' ')); æ check call stack å
IF (res.main = -ProcessSpace) AND (res.family=0) THEN u.newTime:= 1;
SCheck( alarmCond.Signal); æ activate init process å
SCheck( uniGate.Open);
IF u.newTime <> 0 THEN ObjReturn(res);
æ main loop of alarmProc å
SCheck( uniGate.Lock); æ alarmProc stays in the monitor å
REPEAT
NoCheck( alarmCond.Wait(; TimePar));
res:= Call( uniClock.GetClock(; OUT NOW));
IF res.main <> ok THEN NOW:= Ragnarok -1; æ dummy value å
æ NOTE: if uniClock.SetClock is called the alarm signals may not
be generated as expected by the requesting programså
IF u.newTime >= NOW THEN æ small timeOut has been inserted å
u.alarmTime:= u.newTime
ELSE BEGIN
æ scan alarmSet, generate signals, compute next alarmTime å
u.alarmTime:= Ragnarok;
qix:= 1;
WHILE qix <= u.noOfAlarms DO BEGIN
IF qix = 1 THEN BEGIN
SCheck( Copy(alarmSet,proc)); æ next:= first å
END ELSE BEGIN
SCheck( Copy(proc,prvProc));
SCheck( Copy(proc^^.alarmSet,proc)); æ next := next å
END ;
WITH p = proc^^.pData^^ DO BEGIN
IF p.alarmTime = Ragnarok THEN æcancelå remove:= true ELSE
IF p.alarmTime <= NOW THEN BEGIN æ proc has timed out å
SCheck( Copy(proc^^.ProcGrp,group));
SCheck( group^^.grpGate.Lock); æ no speed up å
sigProc(proc,p,group^^.dir,SIGALRM);
SCheck( group^^.grpGate.Open);
remove:= true;
END ELSE
æ compute next alarmtime å
IF p.alarmTime < u.alarmTime THEN BEGIN
u.alarmTime:= p.alarmTime;
remove:= false;
END ;
IF remove THEN BEGIN
p.alarmTime := 0;
u.noOfAlarms:= u.noOfAlarms - 1;
IF qix = 1 THEN BEGIN æ remove first in queue å
SCheck( Copy(proc^^.alarmSet,alarmSet));
END ELSE BEGIN
SCheck( Copy(proc^^.alarmSet,proc));
SCheck( Copy(proc,prvProc^^.alarmSet));
END ;
END ELSE qix:=qix + 1 ;
END æWITH på;
END æWHILEå;
END æ scan å;
u.newTime:= 0;
IF u.alarmTime = Ragnarok THEN BEGIN
TimePar.usec:= 0; æno time outå
END ELSE BEGIN
NOW := u.alarmTime - NOW ; æ #seconds to next time out å
IF NOW > maxSeconds
THEN TimePar.usec:= maxSeconds
ELSE TimePar.usec:= NOW * 1000000 ;
END ;
UNTIL false;
END æWITH uå;
END æPRIVATE ALARMPROCå;
æ$Eå
ENTRY INIT æIN maxStack : sizeType ;
IN procSize : sizeType ;
IN extSixe : sizeType å
æ Called once to initialize UNIPS å
æprocSize < max ==> procs may execute in groupå
æextSize-applicCtxSize < max ==> applications may call groupå
WITH RECORD t:^^;
kpt : ^^;
END ;
VAR res : ResultType; alarmSize : sizeType;
BEGIN
æ#ENTRY# PS('INIT') &ENTRY&å ;
res:= Call( FirstInSet(uniGate,kpt));
IF res.main = ok THEN ObjReturn(univRes(EntryIllegal));
æ Initialize has not been succesfully called å
WITH u = uData^^ DO BEGIN
u.nxtProcID := 2;
u.nxtPipeID := 0;
u.maxStack:= maxStack; æ used when groups are created å
u.procSize:= procSize; æ can call gate/cond/alloc å
u.extSize := extSize ; æ >> maxStack å
SCheck( ClearSize(u.fenvSize));
SCheck( AddEnv(u.fenvSize,noOfFiles));
SCheck( Propagate(rej));
IN æ init may fail å
XCheck( sched.NewGate(OUT uniGate));
XCheck( uniGate.NewCond(OUT alarmCond));
alarmSize:= u.procSize;
SCheck( AddSize(alarmSize,u.procSize)); æ probably ok ? å
XCheck( sched.NewProc(OUT grpOwn; IN alarmSize));
XCheck( uniGate.Lock);
XCheck( DeclProc(grpOwn,alarmProc));
SCheck( Propagate(stop));
SCheck( alarmCond.Wait) ; æ wait for alarmProc to initialize å
IF u.newTime <> 0 THEN exception(univRes(DataValue));
SCheck( uniGate.Open);
SCheck( makeReentrant(void));
res.main:= ok;
DO BEGIN
res:= getException;
NoCheck( Dealloc(uniGate,uniGate));
NoCheck( Dealloc(alarmCond,alarmCond));
NoCheck( Dealloc(grpOwn,grpOwn));
END ;
END æWITH uå;
END æINITå;
æ$Eå
ENTRY INCLUDE æOUT group : PgrpObj ;
IN ShareMaster : refShClFS;
IN termName : text ;
IN loginProg : text å
WITH RECORD t:^^;
tempOwn : ^^;
tempGrp : PgrpEnv;
work : PprocEnv; æ also used as manSet etc. å
END ;
VAR res : ResultType; i : Long; objSize, callStack : sizeType;
BEGIN
æ#ENTRY# PS('INCLUDE') &ENTRY&å ;
WITH u = uData^^ DO BEGIN
RCheck( FirstInSet(uniGate,work)); æ check init ok å
SCheck( Propagate(rej));
SCheck( ClearSize(objSize));
SCheck( AddGen(objSize,refs(grpEnv)));
RCheck( alloc.NewObj(OUT tempOwn; IN objSize, OUT i));
IF u.maxStack = makeSize(-1,-1) THEN callStack:=makeSize(0,0)
ELSE callStack:=u.maxStack;
RCheck( DeclGen(tempOwn,work,tempGrp,refs(grpEnv),
0,makeSize(0,0),u.maxStack,refs(unipsGrp),
bytes(unipsGrp),0,addr(unipsGrp),callStack,true));
SCheck( Copy(tempGrp,tempGrp^^.egoEnv));
SCheck( Copy(tempOwn,tempGrp^^.egoObj));
SCheck( Copy(code,tempGrp^^.code));
SCheck( Copy(egoEnv,tempGrp^^.unix));
RCheck( Copy(tempOwn,group)); æ assign return pointer å
RCheck( sched.NewGate(OUT tempGrp^^.grpGate));
SCheck( MoveMan(tempGrp,grpSet)); æ group becomes visible, work:=NIL å
IN æ group is visible å
XCheck( tempGrp.startGrp(shareMaster; termName, loginProg));
ægroup is readyå
XCheck( MoveOwn(tempOwn,grpOwn));
res.main:= ok;
DO BEGIN æ abort group å
res:= getException;
SCheck( Propagate(stop));
SCheck( uniGate.Lock);
abortGrp(tempGrp,work);
SCheck( uniGate.Open);
END ;
ObjReturn(res);
END æWITH uå;
END æENTRY INCLUDEå;
æ$Eå
ENTRY EXCLUDE æIN group : PgrpObjå
WITH RECORD t:^^;
tempOwn : ^^;
tempGrp : PgrpEnv;
work : PprocEnv;
END ;
VAR res : ResultType; i : Long;
BEGIN
æ#ENTRY# PS('EXCLUDE') &ENTRY&å ;
SCheck( uniGate.Lock);
IN æ gate locked å
XCheck( InspObj(egoEnv,tempGrp,group,i)); æ old param sequence !!!!å
XCheck( MoveOwn(group,tempOwn));
æ group can be deallocated by calling process å
abortGrp(tempGrp,work);
res.main := ok;
DO res:= getException;
SCheck( uniGate.Open);
ObjReturn(res); æ group will be deallocated if temp Owned å
END æENTRY EXCLUDEå;
OTHERWISE UNIPSother
WITH RECORD t:^^;
END;
BEGIN
æ#ENTRY# PS('UNIPSother') &ENTRY&å ;
ObjReturn( univRes(EntryIllegal));
END ;
END æprogram UNIPSimplå;
INITIALIZE
UNIPSimpl 'UNIPS' :
dir 'objDir', alloc 'allocate', sched 'scheduler',
egoEnv '**' , uData
END.
«eof»