MODULE Objects;
IMPORT SYSTEM, Trace, Machine, Heaps, Modules;
CONST
Restart* = 0;
PleaseHalt* = 10;
Unbreakable*= 11;
SelfTermination*=12;
Preempted* = 27;
Resistant* = 28;
Unknown* = 0; Ready* = 1; Running* = 2; AwaitingLock* = 3;
AwaitingCond* = 4; AwaitingEvent* = 5; Suspended* = 6;
Terminated* = 7;
MinPriority = 0;
Low* = 1; Normal* = 2; High* = 3;
GCPriority* = 4;
Realtime* = 5;
NumPriorities = Heaps.NumPriorities;
halt* = 2222;
haltUnbreakable* = 2223;
MinIRQ = Machine.IRQ0;
NumIRQ = Machine.MaxIRQ-MinIRQ+1;
Stats* = FALSE;
TraceVerbose = FALSE;
StrongChecks = FALSE;
VeryConservative = FALSE;
YieldTrick = FALSE;
HandlePriorityInv = TRUE;
InitDiff = MAX(LONGINT);
AddressSize = SYSTEM.SIZEOF(SYSTEM.ADDRESS);
TYPE
CpuCyclesArray* = ARRAY Machine.MaxCPU OF HUGEINT;
EventHandler* = PROCEDURE {DELEGATE};
Timer* = POINTER TO RECORD
next, prev : Timer;
trigger: LONGINT;
handler: EventHandler
END;
ProtectedObject = POINTER TO RECORD END;
ProcessQueue = Heaps.ProcessQueue;
Body = PROCEDURE (self: ProtectedObject);
Condition = PROCEDURE (slink: SYSTEM.ADDRESS): BOOLEAN;
InterruptList = POINTER TO RECORD
next: InterruptList;
handler: EventHandler
END;
TYPE
Process* = OBJECT (Heaps.ProcessLink)
VAR
rootedNext : Process;
obj-: ProtectedObject;
state-: Machine.State;
sse: Machine.SSEState;
sseAdr: SYSTEM.ADDRESS;
condition-: Condition;
condFP-: SYSTEM.ADDRESS;
mode-: LONGINT;
procID-: LONGINT;
waitingOn-: ProtectedObject;
id-: LONGINT;
flags*: SET;
priority-, staticPriority*: LONGINT;
stack*: Machine.Stack;
restartPC-: SYSTEM.ADDRESS;
restartSP-: SYSTEM.ADDRESS;
exp*: Machine.ExceptionState;
oldReturnPC: SYSTEM.ADDRESS;
cpuCycles, lastCpuCycles : CpuCyclesArray;
prioRequests : ARRAY NumPriorities OF LONGINT;
PROCEDURE SetPriority(p : LONGINT);
BEGIN
DEC(prioRequests[staticPriority]);
staticPriority := p;
INC(prioRequests[staticPriority]);
priority := MaxPrio(prioRequests)
END SetPriority;
PROCEDURE FindRoots;
VAR pc, bp, curbp, sp: SYSTEM.ADDRESS; d0, d1: SYSTEM.SIZE; first : BOOLEAN;
BEGIN
IF traceProcess # NIL THEN traceProcess(SELF) END;
IF (priority >= Low) & (priority <= High) & (mode >= Ready) & (mode # Terminated) THEN
IF Heaps.GCType = Heaps.HeuristicStackInspectionGC THEN
IF VeryConservative THEN
Heaps.RegisterCandidates(stack.adr, stack.high-stack.adr)
ELSE
sp := state.SP;
IF sp # 0 THEN
IF Machine.ValidStack(stack, sp) THEN
Heaps.RegisterCandidates(sp, stack.high - sp)
END
ELSE
Trace.String("[Objects.FindRoots sp=0]")
END
END
ELSIF Heaps.GCType = Heaps.MetaDataForStackGC THEN
bp := state.BP; pc := state.PC; first := TRUE;
IF pc # 0 THEN
WHILE (bp # Heaps.NilVal) & (stack.adr <= bp) & (bp < stack.high) DO
FindPointers(bp, pc, d0, d1);
IF first THEN
IF (d0 = 0) OR (d0 = 1) OR (d1 = 3) THEN
IF (d0 = 0) OR (d1 = 3) THEN
SYSTEM.GET(state.SP, pc);
ELSE
SYSTEM.GET(state.SP+AddressSize, pc);
END;
ELSE
curbp := bp;
SYSTEM.GET(curbp, bp);
SYSTEM.GET(curbp+AddressSize, pc);
END;
first := FALSE;
ELSE
curbp := bp;
SYSTEM.GET(curbp, bp);
SYSTEM.GET(curbp+AddressSize, pc);
END
END
END
ELSE
HALT(900)
END
END
END FindRoots;
PROCEDURE FindPointers(bp, pc : SYSTEM.ADDRESS; VAR diff0, diff1: SYSTEM.SIZE);
VAR data: Modules.ProcTableEntry; startIndex, i: LONGINT; ptr : SYSTEM.ADDRESS; success: BOOLEAN;
BEGIN
diff0 := InitDiff; diff1 := InitDiff;
Modules.FindProc(pc, data, startIndex, success);
IF success THEN
diff0 := pc - data.pcFrom;
diff1 := pc - data.pcStatementEnd;
IF (data.noPtr > 0) & (pc >= data.pcStatementBegin) & (pc <= data.pcStatementEnd) THEN
FOR i := 0 TO data.noPtr - 1 DO
SYSTEM.GET(bp + Modules.ptrOffsets[startIndex + i], ptr);
IF ptr # Heaps.NilVal THEN
Heaps.Mark(SYSTEM.VAL(ANY, ptr))
END
END
END
END
END FindPointers;
END Process;
TraceProcess* = PROCEDURE (p: Process);
ExceptionHandler* = PROCEDURE(p: Process; VAR int: Machine.State; VAR exc: Machine.ExceptionState; VAR return: BOOLEAN);
Idle = OBJECT
BEGIN {ACTIVE, SAFE, PRIORITY(-1)} (* negative priority equivalent to MinPriority *)
LOOP
REPEAT
IF ProcessorHLT # NIL THEN ProcessorHLT (* UP *)
ELSE Machine.SpinHint (* MP *)
END
UNTIL maxReady >= lowestAllowedPriority;
Yield
END
END Idle;
Clock = OBJECT
VAR h: Timer;
BEGIN {ACTIVE, SAFE, PRIORITY(High)}
LOOP
Machine.Acquire(Machine.Objects);
LOOP
h := event.next;
IF (h = event) OR (h.trigger - Machine.ticks > 0) THEN EXIT END;
event.next := h.next; event.next.prev := event; (* unlink *)
h.next := NIL; h.prev := NIL;
Machine.Release(Machine.Objects);
h.handler; (* assume handler will return promptly *)
Machine.Acquire(Machine.Objects)
END;
ASSERT(timer = NIL); (* temp strong check *)
timer := running[Machine.ID ()];
timer.mode := AwaitingEvent;
SwitchToNew
END
END Clock;
ReadyProcesses = OBJECT(Heaps.RootObject)
VAR q {UNTRACED}: ARRAY NumPriorities OF ProcessQueue;
PROCEDURE &Init;
VAR i: LONGINT;
BEGIN
FOR i := 0 TO NumPriorities - 1 DO
q[i].head := NIL; q[i].tail := NIL
END
END Init;
PROCEDURE FindRoots;
VAR i: LONGINT;
BEGIN
FOR i := Low TO High DO
Heaps.Mark(q[i].head);
Heaps.Mark(q[i].tail)
END
END FindRoots;
END ReadyProcesses;
GCStatusExt = OBJECT(Heaps.GCStatus)
VAR gcOngoing: BOOLEAN;
PROCEDURE &Init;
BEGIN
gcOngoing := FALSE;
END Init;
PROCEDURE SetgcOngoing(value: BOOLEAN);
VAR p: Process;
BEGIN
IF value THEN
Machine.Acquire(Machine.Objects);
IF ~gcOngoing THEN
gcOngoing := TRUE;
lowestAllowedPriority := GCPriority;
gcBarrier := Machine.allProcessors
END;
p := running[Machine.ID()];
Enter(p);
p.mode := Ready;
SwitchToNew
ELSE
Machine.Acquire(Machine.Objects);
gcOngoing := FALSE;
lowestAllowedPriority := Low;
Machine.Release(Machine.Objects)
END;
END SetgcOngoing;
PROCEDURE GetgcOngoing(): BOOLEAN;
BEGIN
RETURN gcOngoing
END GetgcOngoing;
END GCStatusExt;
GCActivity = OBJECT
BEGIN {ACTIVE, SAFE, PRIORITY(GCPriority)}
UpdateState;
LOOP
Machine.Acquire(Machine.Objects);
ASSERT(gcProcess = NIL); (* temp strong check *)
gcProcess := running[Machine.ID()];
gcProcess.mode := AwaitingEvent;
SwitchToNew; (* SwitchTo called by SwitchToNew will release the lock Machine.Objects *)
(* process is scheduled -> gcProcess = NIL set by scheduler (Timeslice), perform garbage collection now *)
Heaps.CollectGarbage(Modules.root);
Machine.Acquire(Machine.Objects);
IF finalizerProcess # NIL THEN
(* it is safe to move finalizerProcess to the ready queue and set the variable to NIL
since the process has been marked by the GC already - marking is finished here *)
Enter(finalizerProcess);
finalizerProcess := NIL
END;
Machine.Release(Machine.Objects);
Heaps.gcStatus.SetgcOngoing(FALSE)
END
END GCActivity;
FinalizedCollection* = OBJECT
PROCEDURE RemoveAll*(obj: ANY);
BEGIN HALT(301) END RemoveAll;
END FinalizedCollection;
FinalizerNode* = POINTER TO RECORD (Heaps.FinalizerNode)
c*: FinalizedCollection (* base type for collection containing object *)
END;
FinalizerCaller = OBJECT (* separate active object that calls finalizers *)
VAR n: Heaps.FinalizerNode;
BEGIN {ACTIVE, SAFE, PRIORITY(High)}
LOOP
Machine.Acquire(Machine.Objects);
ASSERT(finalizerProcess = NIL); (* temp strong check *)
finalizerProcess := running[Machine.ID()];
finalizerProcess.mode := AwaitingEvent;
SwitchToNew; (* SwitchTo called by SwitchToNew will release the lock Machine.Objects *)
(* process is scheduled -> finalizerProcess = NIL set by GCActivity, perform finalization now *)
LOOP
n := Heaps.GetFinalizer();
IF n = NIL THEN EXIT END;
IF n IS FinalizerNode THEN
n(FinalizerNode).c.RemoveAll(n.objStrong) (* remove it if it is not removed yet *)
END;
IF n.finalizer # NIL THEN
n.finalizer(n.objStrong) (* may acquire locks *)
END
END;
END
END FinalizerCaller;
Interrupter = OBJECT (ProtectedObject)
VAR interruptNumber: LONGINT;
END Interrupter;
VAR
ready: ReadyProcesses;
maxReady: LONGINT;
lowestAllowedPriority: LONGINT;
running-{UNTRACED}: ARRAY Machine.MaxCPU OF Process;
nextProcessID: LONGINT;
gcBarrier: SET;
gcActivity: GCActivity;
gcProcess: Process;
finalizerProcess: Process;
interrupt: ARRAY NumIRQ OF RECORD
root: InterruptList;
process: Process
END;
rootedProcesses: ARRAY NumPriorities OF Process;
event: Timer;
timer : Process;
terminate: PROCEDURE;
trap, trapReturn: ARRAY 2 OF PROCEDURE;
ProcessorHLT*: PROCEDURE;
traceProcess*: TraceProcess;
entry: SYSTEM.ADDRESS;
init: Process;
i: LONGINT;
idlecount*: ARRAY Machine.MaxCPU OF LONGINT;
idleCycles- : ARRAY Machine.MaxCPU OF HUGEINT;
perfTsc: ARRAY Machine.MaxCPU OF HUGEINT;
Nlock-, Nunlock-, Nawait-, NawaitNoIF-, NawaitTrue-, Ncreate-, Nterminate-,
Ncondition-, Ncondition1True-, Ncondition2-, Ncondition2True-,
Ntimeslice-, NtimesliceTaken-, NtimesliceNothing-, NtimesliceIdle-,
NtimesliceKernel-, NtimesliceV86-, NtimesliceCritical-,
Npreempt-, NpreemptTaken-, NpreemptNothing-,
NpreemptKernel-, NpreemptV86-, NpreemptCritical-,
Nenter- : LONGINT;
PROCEDURE GetMaxPrio(VAR queue: ProcessQueue; VAR new: Process);
VAR
t: Heaps.ProcessLink;
maxPriority : LONGINT;
BEGIN
ASSERT(new = NIL);
t := queue.head;
maxPriority := MIN(LONGINT);
WHILE (t # NIL) DO
IF (t(Process).priority > maxPriority) THEN
new := t(Process); maxPriority := t(Process).priority;
END;
t := t.next;
END;
IF new = NIL THEN
ELSE
IF new.next # NIL THEN new.next.prev := new.prev END;
IF new.prev # NIL THEN new.prev.next := new.next END;
IF queue.head = new THEN
queue.head := new.next
END;
IF queue.tail = new THEN
queue.tail := new.prev
END;
new.next := NIL; new.prev := NIL
END;
END GetMaxPrio;
PROCEDURE Get(VAR queue: ProcessQueue; VAR new: Process);
VAR t: Heaps.ProcessLink;
BEGIN
t := queue.head;
IF t = NIL THEN
ELSIF t = queue.tail THEN
queue.head := NIL; queue.tail := NIL
ELSE
queue.head := t.next; t.next := NIL; queue.head.prev := NIL
END;
ASSERT((t = NIL) OR (t.next = NIL) & (t.prev = NIL));
IF t = NIL THEN
new := NIL
ELSE
ASSERT(t IS Process);
new := t(Process)
END;
END Get;
PROCEDURE Put(VAR queue: ProcessQueue; t: Process);
BEGIN
ASSERT((t.next = NIL) & (t.prev = NIL));
IF queue.head = NIL THEN
queue.head := t
ELSE
queue.tail.next := t; t.prev := queue.tail
END;
queue.tail := t
END Put;
PROCEDURE Select(VAR new: Process; priority: LONGINT);
VAR thresholdPrio: LONGINT;
BEGIN
IF Heaps.gcStatus.GetgcOngoing() THEN
thresholdPrio := GCPriority
ELSE
thresholdPrio := priority
END;
LOOP
IF maxReady < thresholdPrio THEN
IF priority < thresholdPrio THEN Get(ready.q[MinPriority], new) ELSE new := NIL END;
EXIT
END;
Get(ready.q[maxReady], new);
IF (new # NIL) OR (maxReady = MinPriority) THEN EXIT END;
DEC(maxReady)
END
END Select;
PROCEDURE Enter(t: Process);
BEGIN
IF Stats THEN Machine.AtomicInc(Nenter) END;
t.mode := Ready;
Put(ready.q[t.priority], t);
IF t.priority > maxReady THEN
maxReady := t.priority
END
END Enter;
PROCEDURE Remove(VAR queue: ProcessQueue; t: Process);
BEGIN
IF t.prev # NIL THEN t.prev.next := t.next END;
IF t.next # NIL THEN t.next.prev := t.prev END;
IF t = queue.head THEN queue.head := t.next END;
IF t = queue.tail THEN queue.tail := t.prev END;
ASSERT((queue.head = NIL) OR (queue.head.prev = NIL) & (queue.tail.next = NIL));
t.prev := NIL;
t.next := NIL
END Remove;
PROCEDURE SwitchTo(VAR running: Process; new: Process);
VAR id: LONGINT;
BEGIN
ASSERT(Machine.CS () MOD 4 = Machine.UserLevel);
id := Machine.ID ();
INC (running.cpuCycles[id], Machine.GetTimer () - perfTsc[id]);
IF running.priority = MinPriority THEN
INC (idleCycles[id], Machine.GetTimer () - perfTsc[id]);
END;
running.state.PC := Machine.CurrentPC ();
running.state.SP := Machine.CurrentSP ();
running.state.BP := Machine.CurrentBP ();
IF Machine.SSESupport THEN Machine.SSESaveMin(running.sseAdr)
ELSE Machine.FPUSaveMin(running.sse)
END;
running := new; new.mode := Running;
IF Preempted IN new.flags THEN
ASSERT(new.state.CS MOD 4 = Machine.UserLevel);
EXCL(new.flags, Preempted);
perfTsc[id] := Machine.GetTimer ();
Machine.SetSP (new.state.SP);
Machine.PushState(new.state);
IF Machine.SSESupport THEN Machine.SSERestoreFull(new.sseAdr)
ELSE Machine.FPURestoreFull(new.sse)
END;
Machine.Release(Machine.Objects);
Machine.JumpState
ELSE
IF Machine.SSESupport THEN Machine.SSERestoreMin(new.sseAdr)
ELSE Machine.FPURestoreMin(new.sse)
END;
perfTsc[id] := Machine.GetTimer ();
Machine.SetSP (new.state.SP);
Machine.SetBP (new.state.BP);
Machine.Release(Machine.Objects);
END;
END SwitchTo;
PROCEDURE SwitchToNew;
VAR new: Process;
BEGIN
Select(new, MinPriority);
new.procID := Machine.ID ();
SwitchTo(running[new.procID], new)
END SwitchToNew;
PROCEDURE Yield*;
VAR r, new: Process;
BEGIN
IF ~YieldTrick OR (maxReady >= lowestAllowedPriority) THEN
r := SYSTEM.VAL (Process, Machine.GetProcessPtr ());
Machine.Acquire(Machine.Objects);
Select(new, r.priority);
IF new # NIL THEN
Enter(r);
new.procID := Machine.ID ();
SwitchTo(running[new.procID], new)
ELSE
Machine.Release(Machine.Objects)
END
END
END Yield;
PROCEDURE SwitchToState(new: Process; VAR state: Machine.State);
BEGIN
state.SP := new.state.BP+AddressSize*4;
SYSTEM.GET (new.state.BP, state.BP);
SYSTEM.GET (new.state.BP + AddressSize, state.PC);
END SwitchToState;
PROCEDURE Timeslice*(VAR state: Machine.State);
VAR id: LONGINT; new: Process;
BEGIN
Machine.Acquire(Machine.Objects);
IF Stats THEN Machine.AtomicInc(Ntimeslice) END;
id := Machine.ID ();
IF id = 0 THEN
IF event.next.trigger - Machine.ticks <= 0 THEN
IF event.next # event THEN
IF timer # NIL THEN
ASSERT(timer.mode = AwaitingEvent);
Enter(timer); timer := NIL
END
ELSE
event.trigger := Machine.ticks + MAX(LONGINT) DIV 2
END
END
END;
IF Heaps.gcStatus.GetgcOngoing() & (id IN gcBarrier) THEN
EXCL(gcBarrier, id);
IF gcBarrier = {} THEN
ASSERT(gcProcess.mode = AwaitingEvent);
Enter(gcProcess); gcProcess := NIL
END
END;
IF Machine.PreemptCount(id) = 1 THEN
IF ~(Machine.VMBit IN state.FLAGS) THEN
IF state.CS MOD 4 = Machine.UserLevel THEN
IF running[id].priority # MinPriority THEN
Select(new, running[id].priority);
IF new # NIL THEN
ASSERT(Machine.CS () MOD 4 = Machine.KernelLevel);
INC (running[id].cpuCycles[id], Machine.GetTimer () - perfTsc[id]);
IF Stats THEN Machine.AtomicInc(NtimesliceTaken) END;
INCL(running[id].flags, Preempted);
Machine.CopyState(state, running[id].state);
IF Machine.SSESupport THEN Machine.SSESaveFull(running[id].sseAdr)
ELSE Machine.FPUSaveFull(running[id].sse);
END;
Enter(running[id]);
running[id] := new;
new.mode := Running; new.procID := id;
IF Preempted IN new.flags THEN
EXCL(new.flags, Preempted);
Machine.CopyState(new.state, state);
IF Machine.SSESupport THEN Machine.SSERestoreFull(new.sseAdr)
ELSE Machine.FPURestoreFull(new.sse)
END
ELSE
SwitchToState(new, state);
IF Machine.SSESupport THEN Machine.SSERestoreMin(new.sseAdr)
ELSE Machine.FPURestoreMin(new.sse)
END
END;
perfTsc[id] := Machine.GetTimer ()
ELSE
IF Stats THEN Machine.AtomicInc(NtimesliceNothing) END;
END;
IF PleaseHalt IN running[id].flags THEN
DEC(state.SP, AddressSize);
SYSTEM.PUT (state.SP, state.PC);
IF (Unbreakable IN running[id].flags) THEN
state.PC := SYSTEM.VAL (SYSTEM.ADDRESS, trap[1]);
ELSE
state.PC := SYSTEM.VAL (SYSTEM.ADDRESS, trap[0]);
END;
END;
ELSE
INC(idlecount[id]);
IF Stats THEN Machine.AtomicInc(NtimesliceIdle) END;
END
ELSE
IF Stats THEN Machine.AtomicInc(NtimesliceKernel) END
END
ELSE
IF Stats THEN Machine.AtomicInc(NtimesliceV86) END
END
ELSE
IF Stats THEN Machine.AtomicInc(NtimesliceCritical) END
END;
Machine.Release(Machine.Objects)
END Timeslice;
PROCEDURE CurrentProcess*( ): Process;
BEGIN
RETURN SYSTEM.VAL(Process, Machine.GetProcessPtr());
END CurrentProcess;
PROCEDURE GetStackBottom*(p: Process): SYSTEM.ADDRESS;
BEGIN
RETURN p.stack.high
END GetStackBottom;
PROCEDURE ActiveObject* (): ANY;
VAR r: Process;
BEGIN
r := SYSTEM.VAL(Process, Machine.GetProcessPtr ());
RETURN r.obj
END ActiveObject;
PROCEDURE GetProcessID* (): LONGINT;
VAR r: Process;
BEGIN
r := SYSTEM.VAL (Process, Machine.GetProcessPtr ());
RETURN r.id
END GetProcessID;
PROCEDURE SetPriority*(priority: LONGINT);
VAR id: LONGINT;
BEGIN
ASSERT((priority >= Low) & (priority <= Realtime));
IF HandlePriorityInv THEN
Machine.Acquire(Machine.Objects);
id := Machine.ID();
running[id].SetPriority(priority);
Machine.Release(Machine.Objects)
ELSE
id := Machine.AcquirePreemption ();
running[id].priority := priority;
Machine.ReleasePreemption
END
END SetPriority;
PROCEDURE LockedByCurrent*(obj: ANY): BOOLEAN;
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; id: LONGINT; res: BOOLEAN;
BEGIN
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
ASSERT(hdr IS Heaps.ProtRecBlock);
IF HandlePriorityInv THEN
Machine.Acquire(Machine.Objects);
id := Machine.ID();
res := (hdr.lockedBy = running[id]);
Machine.Release(Machine.Objects)
ELSE
id := Machine.AcquirePreemption ();
Machine.AcquireObject(hdr.locked);
res := (hdr.lockedBy = running[id]);
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
END;
RETURN res
END LockedByCurrent;
PROCEDURE NumReady* (): LONGINT;
VAR i, n: LONGINT; p: Heaps.ProcessLink;
BEGIN
n := 0;
Machine.Acquire(Machine.Objects);
FOR i := MinPriority+1 TO NumPriorities-1 DO
p := ready.q[i].head; WHILE p # NIL DO INC(n); p := p.next END
END;
FOR i := 0 TO Machine.MaxCPU-1 DO
IF (running[i] # NIL) & (running[i].priority > MinPriority) THEN INC(n) END
END;
Machine.Release(Machine.Objects);
RETURN n
END NumReady;
PROCEDURE GetCpuCycles*(process : Process; VAR cpuCycles : CpuCyclesArray; all : BOOLEAN);
VAR i : LONGINT;
BEGIN
ASSERT(process # NIL);
FOR i := 0 TO Machine.MaxCPU-1 DO cpuCycles[i] := process.cpuCycles[i]; END;
IF ~all THEN
FOR i := 0 TO Machine.MaxCPU-1 DO
cpuCycles[i] := cpuCycles[i] - process.lastCpuCycles[i];
process.lastCpuCycles[i] := process.cpuCycles[i];
END;
END;
END GetCpuCycles;
PROCEDURE FieldIRQ(VAR state: Machine.State);
VAR t: Process; id: LONGINT; new: Process; preempt: BOOLEAN;
BEGIN
Machine.Sti ();
Machine.DisableIRQ(state.INT);
Machine.Acquire(Machine.Objects);
t := interrupt[state.INT-MinIRQ].process;
IF StrongChecks THEN ASSERT(t.mode = AwaitingEvent) END;
id := Machine.ID ();
preempt := (t.priority > maxReady) & (maxReady # MinPriority) & (t.priority > running[id].priority);
Enter(t);
IF preempt THEN
IF Stats THEN Machine.AtomicInc(Npreempt) END;
IF Machine.PreemptCount(id) = 1 THEN
IF ~(Machine.VMBit IN state.FLAGS) THEN
IF state.CS MOD 4 = Machine.UserLevel THEN
Select(new, running[id].priority + 1);
IF new # NIL THEN
ASSERT(Machine.CS () MOD 4 = Machine.KernelLevel);
INC (running[id].cpuCycles[id], Machine.GetTimer () - perfTsc[id]);
IF running[id].priority = MinPriority THEN
INC (idleCycles[id], Machine.GetTimer () - perfTsc[id]);
END;
IF Stats THEN Machine.AtomicInc(NpreemptTaken) END;
INCL(running[id].flags, Preempted);
Machine.CopyState(state, running[id].state);
IF Machine.SSESupport THEN Machine.SSESaveFull(running[id].sseAdr)
ELSE Machine.FPUSaveFull(running[id].sse);
END;
Enter(running[id]);
running[id] := new;
new.mode := Running; new.procID := id;
IF Preempted IN new.flags THEN
EXCL(new.flags, Preempted);
Machine.CopyState(new.state, state);
IF Machine.SSESupport THEN Machine.SSERestoreFull(new.sseAdr)
ELSE Machine.FPURestoreFull(new.sse)
END
ELSE
SwitchToState(new, state);
IF Machine.SSESupport THEN Machine.SSERestoreMin(new.sseAdr)
ELSE Machine.FPURestoreMin(new.sse)
END
END;
perfTsc[id] := Machine.GetTimer ()
ELSE
IF Stats THEN Machine.AtomicInc(NpreemptNothing) END
END
ELSE
IF Stats THEN Machine.AtomicInc(NpreemptKernel) END
END
ELSE
IF Stats THEN Machine.AtomicInc(NpreemptV86) END
END
ELSE
IF Stats THEN Machine.AtomicInc(NpreemptCritical) END
END
END;
Machine.Release(Machine.Objects)
END FieldIRQ;
PROCEDURE InterruptProcess(self: ProtectedObject);
VAR h: InterruptList; t: Process; int: LONGINT;
BEGIN
int := self(Interrupter).interruptNumber;
t := interrupt[int-MinIRQ].process;
LOOP
h := interrupt[int-MinIRQ].root;
WHILE h # NIL DO h.handler (); h := h.next END;
Machine.Acquire(Machine.Objects);
ASSERT(running[Machine.ID ()] = t);
t.mode := AwaitingEvent;
Machine.EnableIRQ(int);
SwitchToNew
END
END InterruptProcess;
PROCEDURE InstallHandler*(h: EventHandler; int: LONGINT);
VAR t: Process; new: BOOLEAN; ih: Interrupter; n: InterruptList; i: LONGINT;
BEGIN
ASSERT((int >= MinIRQ) & (int-MinIRQ < NumIRQ));
IF interrupt[int-MinIRQ].process = NIL THEN
NEW(ih); ih.interruptNumber := int;
NewProcess(InterruptProcess, {Resistant}, ih, t);
t.priority := High;
t.staticPriority := t.priority;
FOR i := 0 TO LEN(t.prioRequests) - 1 DO t.prioRequests[i] := 0 END;
INC(t.prioRequests[t.priority])
END;
NEW(n); n.handler := h;
Machine.Acquire(Machine.Objects);
IF interrupt[int-MinIRQ].process = NIL THEN
t.id := nextProcessID; INC(nextProcessID);
t.mode := AwaitingEvent;
interrupt[int-MinIRQ].process := t;
new := TRUE
ELSE
new := FALSE
END;
n.next := interrupt[int-MinIRQ].root;
interrupt[int-MinIRQ].root := n;
Machine.Release(Machine.Objects);
IF new THEN Machine.InstallHandler(FieldIRQ, int) END
END InstallHandler;
PROCEDURE RemoveHandler*(h: EventHandler; int: LONGINT);
VAR p, c: InterruptList;
BEGIN
ASSERT((int >= MinIRQ) & (int-MinIRQ < NumIRQ));
Machine.Acquire(Machine.Objects);
p := NIL; c := interrupt[int-MinIRQ].root;
WHILE (c.handler # h) & (c # NIL) DO p := c; c := c.next END;
IF c.handler = h THEN
IF p = NIL THEN
interrupt[int-MinIRQ].root := c.next;
ELSE
p.next := c.next
END
ELSE
HALT(99);
END;
Machine.Release(Machine.Objects)
END RemoveHandler;
PROCEDURE SetTimeoutAbsOrRel(t: Timer; h: EventHandler; ms: LONGINT; isAbsolute: BOOLEAN);
VAR e: Timer; trigger: LONGINT;
BEGIN
ASSERT(Machine.Second= 1000);
ASSERT((t # NIL) & (h # NIL));
IF ms < 1 THEN ms := 1 END;
Machine.Acquire(Machine.Objects);
IF isAbsolute THEN trigger := ms ELSE trigger := Machine.ticks + ms END;
IF t.next # NIL THEN
t.next.prev := t.prev; t.prev.next := t.next
END;
t.trigger := trigger; t.handler := h;
e := event.next;
WHILE (e # event) & (e.trigger - trigger <= 0) DO e := e.next END;
t.prev := e.prev; e.prev := t; t.next := e; t.prev.next := t;
Machine.Release(Machine.Objects)
END SetTimeoutAbsOrRel;
PROCEDURE SetTimeout*(t: Timer; h: EventHandler; ms: LONGINT);
BEGIN
SetTimeoutAbsOrRel(t, h, ms, FALSE)
END SetTimeout;
PROCEDURE SetTimeoutAt*(t: Timer; h: EventHandler; ms: LONGINT);
BEGIN
SetTimeoutAbsOrRel(t, h, ms, TRUE)
END SetTimeoutAt;
PROCEDURE CancelTimeout*(t: Timer);
BEGIN
Machine.Acquire(Machine.Objects);
ASSERT(t # event);
IF t.next # NIL THEN
t.next.prev := t.prev; t.prev.next := t.next;
t.next := NIL; t.prev := NIL
END;
Machine.Release(Machine.Objects)
END CancelTimeout;
PROCEDURE Terminate*;
VAR id: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nterminate) END;
Machine.Acquire(Machine.Objects);
id := Machine.ID ();
running[id].mode := Terminated;
SwitchToNew;
HALT(2201)
END Terminate;
PROCEDURE Halt;
BEGIN
HALT(halt);
END Halt;
PROCEDURE HaltUnbreakable;
BEGIN
HALT(haltUnbreakable);
END HaltUnbreakable;
PROCEDURE HaltAltPC(haltCode: LONGINT);
VAR bp: SYSTEM.ADDRESS; p: Process;
BEGIN
p := running[Machine.ID ()];
ASSERT(p.oldReturnPC # -1);
bp := Machine.CurrentBP ();
SYSTEM.PUT (bp + AddressSize, p.oldReturnPC);
CASE haltCode OF
|halt: HALT(halt);
|haltUnbreakable: HALT(haltUnbreakable);
END;
END HaltAltPC;
PROCEDURE HaltReturn;
VAR bp: SYSTEM.ADDRESS;
BEGIN
bp := Machine.CurrentBP ();
SYSTEM.GET (bp, bp);
Machine.SetBP (bp);
HaltAltPC(halt);
END HaltReturn;
PROCEDURE HaltUnbreakableReturn;
VAR bp: SYSTEM.ADDRESS;
BEGIN
bp := Machine.CurrentBP ();
SYSTEM.GET (bp, bp);
Machine.SetBP (bp);
HaltAltPC(haltUnbreakable);
END HaltUnbreakableReturn;
PROCEDURE TerminateThis*(t: Process; unbreakable: BOOLEAN);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; pc, fp : SYSTEM.ADDRESS;
PROCEDURE TerminateAwaiting(t: Process);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock;
BEGIN
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, t.waitingOn) + Heaps.HeapBlockOffset, hdr);
ASSERT(hdr IS Heaps.ProtRecBlock);
IF t.mode = AwaitingLock THEN
fp := t.state.BP;
SYSTEM.GET (fp, fp);
SYSTEM.GET (fp, fp);
SYSTEM.GET (fp + AddressSize, pc);
IF ~Modules.IsExceptionHandled(pc, fp, FALSE) THEN
Remove(hdr.awaitingLock, t);
t.waitingOn := NIL; SYSTEM.GET (t.state.BP + AddressSize, t.oldReturnPC);
IF unbreakable THEN
SYSTEM.PUT (t.state.BP + AddressSize, SYSTEM.VAL (SYSTEM.ADDRESS, trapReturn[1]))
ELSE
SYSTEM.PUT (t.state.BP + AddressSize, SYSTEM.VAL (SYSTEM.ADDRESS, trapReturn[0]))
END;
Enter(t)
ELSE
Machine.Acquire (Machine.TraceOutput);
Trace.String(" Not allowed to kill "); Trace.Int(t.id, 1); Trace.Char(" "); Trace.Int(t.mode, 1); Trace.Ln;
Machine.Release (Machine.TraceOutput);
END
ELSIF t.mode = AwaitingCond THEN
SYSTEM.GET (t.state.BP, fp);
SYSTEM.GET (t.state.PC, pc);
IF ~Modules.IsExceptionHandled(pc, fp, TRUE) THEN
Remove(hdr.awaitingCond, t);
t.waitingOn := NIL; SYSTEM.GET (t.state.BP + AddressSize, t.oldReturnPC);
IF unbreakable THEN
SYSTEM.PUT (t.state.BP + AddressSize, SYSTEM.VAL (SYSTEM.ADDRESS, trapReturn[1]))
ELSE
SYSTEM.PUT (t.state.BP + AddressSize, SYSTEM.VAL (SYSTEM.ADDRESS, trapReturn[0]))
END;
Enter(t)
ELSE
Machine.Acquire (Machine.TraceOutput);
Trace.String(" Not allowed to kill "); Trace.Int(t.id, 1); Trace.Char(" "); Trace.Int(t.mode, 1); Trace.Ln;
Machine.Release (Machine.TraceOutput);
END
END
END TerminateAwaiting;
BEGIN
IF PleaseHalt IN t.flags THEN
IF TraceVerbose THEN
Machine.Acquire (Machine.TraceOutput);
Trace.String("Process (ID="); Trace.Int(t.id, 0); Trace.StringLn (") is already halting!");
Machine.Release (Machine.TraceOutput);
END;
RETURN
ELSE
Machine.Acquire(Machine.Objects);
IF (t = running[Machine.ID ()]) THEN INCL(t.flags, SelfTermination); END;
IF TraceVerbose THEN
Machine.Acquire (Machine.TraceOutput);
Trace.String(" Kill "); Trace.Int(t.id, 1); Trace.Char(" "); Trace.Int(t.mode, 1); Trace.Ln;
Machine.Release (Machine.TraceOutput);
END;
CASE t.mode OF
|Running:
INCL(t.flags, PleaseHalt);
IF unbreakable THEN INCL(t.flags, Unbreakable) END
|Ready:
DEC(t.state.SP, AddressSize); SYSTEM.PUT (t.state.SP, t.state.PC);
IF unbreakable THEN t.state.PC := SYSTEM.VAL (SYSTEM.ADDRESS, trap[1])
ELSE t.state.PC := SYSTEM.VAL (SYSTEM.ADDRESS, trap[0]) END
|AwaitingLock, AwaitingCond:
IF HandlePriorityInv THEN
TerminateAwaiting(t)
ELSE
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, t.waitingOn) + Heaps.HeapBlockOffset, hdr);
ASSERT(hdr IS Heaps.ProtRecBlock);
IF ~hdr.locked THEN
Machine.AcquireObject(hdr.locked);
TerminateAwaiting(t);
Machine.ReleaseObject(hdr.locked)
END
END
| AwaitingEvent, Unknown, Terminated:
END;
Machine.Release(Machine.Objects)
END
END TerminateThis;
PROCEDURE FinalizeProcess(t: ANY);
BEGIN
Machine.DisposeStack(t(Process).stack)
END FinalizeProcess;
PROCEDURE NewProcess(body: Body; flags: SET; obj: ProtectedObject; VAR new: Process);
VAR t: Process; sp: SYSTEM.ADDRESS; id: LONGINT; fn: Heaps.FinalizerNode;
BEGIN
NEW(t); NEW(fn);
t.next := NIL; t.prev := NIL; t.rootedNext := NIL;
t.waitingOn := NIL; t.flags := flags;
t.obj := obj; t.mode := Unknown;
Machine.NewStack(t.stack, t, sp);
IF VeryConservative THEN
Machine.Fill32(t.stack.adr, sp-t.stack.adr, SHORT(0D0D0DEADH))
END;
SYSTEM.PUT (sp-1*AddressSize, obj);
SYSTEM.PUT (sp-2*AddressSize, terminate);
SYSTEM.PUT (sp-5*AddressSize, body);
SYSTEM.PUT (sp-6*AddressSize, NIL);
t.sseAdr := SYSTEM.ADR(t.sse) + ((-SYSTEM.ADR(t.sse)) MOD 16);
IF Machine.SSESupport THEN Machine.SSESaveMin(t.sseAdr)
ELSE Machine.FPUSaveMin(t.sse)
END;
t.state.BP := sp-6*AddressSize;
t.state.SP := t.state.BP;
t.state.PC := 0;
IF Restart IN flags THEN
t.restartPC := SYSTEM.VAL (SYSTEM.ADDRESS, body);
t.restartSP := sp-2*AddressSize
ELSE
t.restartPC := SYSTEM.VAL (SYSTEM.ADDRESS, terminate);
t.restartSP := sp
END;
fn.finalizer := FinalizeProcess;
Heaps.AddFinalizer(t, fn);
FOR id := 0 TO Machine.MaxCPU-1 DO t.cpuCycles[id] := 0 END;
new := t
END NewProcess;
PROCEDURE CreateProcess*(body: Body; priority: LONGINT; flags: SET; obj: ProtectedObject);
VAR t: Process; type: SYSTEM.ADDRESS; heapBlock {UNTRACED}: Heaps.HeapBlock; i: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Ncreate) END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, heapBlock);
ASSERT(heapBlock IS Heaps.ProtRecBlock);
SYSTEM.GET (SYSTEM.VAL (SYSTEM.ADDRESS, obj) + Heaps.TypeDescOffset, type);
IF Restart IN flags THEN INCL(flags, Resistant) END;
NewProcess(body, flags, obj, t);
Machine.Acquire(Machine.Objects);
t.id := nextProcessID; INC(nextProcessID);
IF priority = 0 THEN
t.priority := running[Machine.ID ()].priority
ELSIF priority > 0 THEN
t.priority := priority
ELSE
t.priority := MinPriority
END;
t.staticPriority := t.priority;
FOR i := 0 TO LEN(t.prioRequests) - 1 DO t.prioRequests[i] := 0 END;
INC(t.prioRequests[t.priority]);
CASE t.priority OF
MinPriority : t.rootedNext := rootedProcesses[t.priority]; rootedProcesses[t.priority] := t
| Low, Normal, High :
| GCPriority, Realtime : t.rootedNext := rootedProcesses[t.priority]; rootedProcesses[t.priority] := t
END;
Enter(t);
Machine.Release(Machine.Objects)
END CreateProcess;
PROCEDURE Lock*(obj: ProtectedObject; exclusive: BOOLEAN);
BEGIN
IF HandlePriorityInv THEN
LockPriorityInv(obj, exclusive)
ELSE
LockNoPriorityInv(obj, exclusive)
END
END Lock;
PROCEDURE LockNoPriorityInv(obj: ProtectedObject; exclusive: BOOLEAN);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r: Process; id: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nlock) END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
IF StrongChecks THEN
ASSERT(hdr IS Heaps.ProtRecBlock);
ASSERT(exclusive)
END;
id := Machine.AcquirePreemption ();
Machine.AcquireObject(hdr.locked);
IF hdr.count = 0 THEN
hdr.count := -1; hdr.lockedBy := SYSTEM.VAL (Process, Machine.GetProcessPtr ());
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
ELSE
r := SYSTEM.VAL (Process, Machine.GetProcessPtr ());
IF hdr.lockedBy = r THEN
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
ASSERT(hdr.lockedBy # r, 2203);
END;
ASSERT(r.waitingOn = NIL);
r.waitingOn := obj; r.mode := AwaitingLock;
Machine.Acquire(Machine.Objects);
Put(hdr.awaitingLock, r);
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
SwitchToNew
END
END LockNoPriorityInv;
PROCEDURE PropagatePrio(hdr: Heaps.ProtRecBlock; prevMaxWaitingPrio, waitingPrio: LONGINT);
VAR propagateFurther: BOOLEAN; p: Process; obj: ProtectedObject;
BEGIN
propagateFurther := TRUE;
WHILE propagateFurther & (waitingPrio > prevMaxWaitingPrio) DO
IF hdr.lockedBy # NIL THEN
p := hdr.lockedBy(Process);
DEC(p.prioRequests[prevMaxWaitingPrio]);
INC(p.prioRequests[waitingPrio]);
IF (p.waitingOn # NIL) & (waitingPrio > p.priority) THEN
obj := p.waitingOn;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
prevMaxWaitingPrio := MaxPrio(hdr.waitingPriorities);
DEC(hdr.waitingPriorities[p.priority]);
INC(hdr.waitingPriorities[waitingPrio]);
ELSE
propagateFurther := FALSE
END;
IF waitingPrio > p.priority THEN
IF p.mode = Ready THEN Remove(ready.q[p.priority], p) END;
p.priority := waitingPrio;
IF p.mode = Ready THEN Enter(p) END;
END
ELSE
propagateFurther := FALSE
END
END
END PropagatePrio;
PROCEDURE LockPriorityInv(obj: ProtectedObject; exclusive: BOOLEAN);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r: Process;
maxWaitingPrio, prevMaxWaitingPrio: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nlock) END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
IF StrongChecks THEN
ASSERT(hdr IS Heaps.ProtRecBlock);
ASSERT(exclusive)
END;
Machine.Acquire(Machine.Objects);
r := SYSTEM.VAL(Process, Machine.GetProcessPtr());
IF hdr.count = 0 THEN
hdr.count := -1; hdr.lockedBy := r;
maxWaitingPrio := MaxPrio(hdr.waitingPriorities);
INC(r.prioRequests[maxWaitingPrio]);
r.priority := MaxPrio(r.prioRequests);
Machine.Release(Machine.Objects);
ELSE
IF hdr.lockedBy = r THEN
Machine.Release(Machine.Objects);
ASSERT(hdr.lockedBy # r, 2203);
END;
IF r.waitingOn # NIL THEN
Machine.Acquire(Machine.TraceOutput);
Trace.String("Objects: LockPriorityInv - hdr.count # NIL, but r.waitingOn # NIL");
Machine.Release(Machine.TraceOutput)
END;
ASSERT(r.waitingOn = NIL);
r.waitingOn := obj; r.mode := AwaitingLock;
prevMaxWaitingPrio := MaxPrio(hdr.waitingPriorities);
INC(hdr.waitingPriorities[r.priority]);
IF r.priority > prevMaxWaitingPrio THEN PropagatePrio(hdr, prevMaxWaitingPrio, r.priority) END;
Put(hdr.awaitingLock, r);
SwitchToNew
END
END LockPriorityInv;
PROCEDURE FindCondition(VAR q: ProcessQueue): Process;
VAR first, cand: Process;
BEGIN
IF Stats THEN Machine.AtomicInc(Ncondition) END;
Get(q, first);
IF first.condition(first.condFP) THEN
IF Stats THEN Machine.AtomicInc(Ncondition1True) END;
RETURN first
END;
Put(q, first);
WHILE q.head # first DO
IF Stats THEN Machine.AtomicInc(Ncondition2) END;
Get(q, cand);
IF cand.condition(cand.condFP) THEN
IF Stats THEN Machine.AtomicInc(Ncondition2True) END;
RETURN cand
END;
Put(q, cand)
END;
RETURN NIL
END FindCondition;
PROCEDURE MaxPrio(CONST priorityCounts: ARRAY OF LONGINT): LONGINT;
VAR i: LONGINT;
BEGIN
i := LEN(priorityCounts) - 1;
WHILE (i >= 0) & (priorityCounts[i] = 0) DO DEC(i) END;
IF priorityCounts[i] = 0 THEN
Machine.Acquire(Machine.TraceOutput);
Trace.StringLn("Objects: MaxPrio - SEVERE ERROR: priorityCounts contains all zeros");
Machine.Release(Machine.TraceOutput);
END;
RETURN i
END MaxPrio;
PROCEDURE Unlock*(obj: ProtectedObject; dummy: BOOLEAN);
BEGIN
IF HandlePriorityInv THEN
UnlockPriorityInv(obj)
ELSE
UnlockNoPriorityInv(obj)
END
END Unlock;
PROCEDURE TransferLock(hdr: Heaps.ProtRecBlock; p: Process);
VAR maxWaitingPrio: LONGINT;
BEGIN
p.waitingOn := NIL; hdr.lockedBy := p;
IF HandlePriorityInv THEN
DEC(hdr.waitingPriorities[p.priority]);
maxWaitingPrio := MaxPrio(hdr.waitingPriorities);
INC(p.prioRequests[maxWaitingPrio]);
p.priority := MaxPrio(p.prioRequests)
END
END TransferLock;
PROCEDURE UnlockNoPriorityInv(obj: ProtectedObject);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; t, c, r: Process; id: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nunlock) END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
IF StrongChecks THEN
ASSERT(hdr IS Heaps.ProtRecBlock)
END;
ASSERT(hdr.count = -1);
IF hdr.awaitingCond.head # NIL THEN
c := FindCondition(hdr.awaitingCond)
ELSE
c := NIL
END;
id := Machine.AcquirePreemption ();
Machine.AcquireObject(hdr.locked);
r := running[Machine.ID ()];
IF hdr.lockedBy # r THEN
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
ASSERT(hdr.lockedBy = r)
END;
IF c = NIL THEN
Get(hdr.awaitingLock, t);
IF t # NIL THEN
IF StrongChecks THEN
ASSERT((t.mode = AwaitingLock) & (t.waitingOn = obj))
END;
TransferLock(hdr, t)
ELSE
hdr.lockedBy := NIL; hdr.count := 0
END
ELSE
TransferLock(hdr, c);
t := NIL
END;
Machine.ReleaseObject(hdr.locked);
IF (c # NIL) OR (t # NIL) THEN
Machine.Acquire(Machine.Objects);
IF c # NIL THEN Enter(c) END;
IF t # NIL THEN Enter(t) END;
Machine.Release(Machine.Objects);
END;
Machine.ReleasePreemption;
END UnlockNoPriorityInv;
PROCEDURE UnlockPriorityInv(obj: ProtectedObject);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; t, c, r: Process; maxWaitingPrio: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nunlock) END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
IF StrongChecks THEN
ASSERT(hdr IS Heaps.ProtRecBlock)
END;
ASSERT(hdr.count = -1);
IF hdr.awaitingCond.head # NIL THEN
c := FindCondition(hdr.awaitingCond)
ELSE
c := NIL
END;
Machine.Acquire(Machine.Objects);
r := running[Machine.ID ()];
IF hdr.lockedBy # r THEN
Machine.Release(Machine.Objects);
ASSERT(hdr.lockedBy = r)
END;
maxWaitingPrio := MaxPrio(hdr.waitingPriorities);
DEC(r.prioRequests[maxWaitingPrio]);
r.priority := MaxPrio(r.prioRequests);
IF c = NIL THEN
GetMaxPrio(hdr.awaitingLock, t);
IF t = NIL THEN
hdr.lockedBy := NIL; hdr.count := 0
ELSE
IF StrongChecks THEN ASSERT((t.mode = AwaitingLock) & (t.waitingOn = obj)) END;
TransferLock(hdr, t)
END
ELSE
TransferLock(hdr, c);
t := NIL
END;
IF (c # NIL) OR (t # NIL) THEN
IF c # NIL THEN Enter(c) END;
IF t # NIL THEN Enter(t) END;
END;
Machine.Release(Machine.Objects);
END UnlockPriorityInv;
PROCEDURE Await*(cond: Condition; slink: SYSTEM.ADDRESS; obj: ProtectedObject; flags: SET);
BEGIN
IF HandlePriorityInv THEN
AwaitPriorityInv(cond, slink, obj, flags)
ELSE
AwaitNoPriorityInv(cond, slink, obj, flags)
END
END Await;
PROCEDURE AwaitNoPriorityInv(cond: Condition; slink: SYSTEM.ADDRESS; obj: ProtectedObject; flags: SET);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r, c, t: Process; id: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nawait) END;
IF 1 IN flags THEN
IF Stats THEN Machine.AtomicInc(NawaitNoIF) END;
IF cond(slink) THEN
IF Stats THEN Machine.AtomicInc(NawaitTrue) END;
RETURN
END
END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
IF StrongChecks THEN
ASSERT(hdr IS Heaps.ProtRecBlock)
END;
id := Machine.AcquirePreemption ();
Machine.AcquireObject(hdr.locked);
r := running[id];
IF hdr.lockedBy = r THEN
IF StrongChecks THEN ASSERT(hdr.count = -1) END;
IF hdr.awaitingCond.head # NIL THEN
c := FindCondition(hdr.awaitingCond)
ELSE
c := NIL
END;
IF c = NIL THEN
Get(hdr.awaitingLock, t);
IF t = NIL THEN
hdr.count := 0; hdr.lockedBy := NIL;
ELSE
IF StrongChecks THEN ASSERT(t.mode = AwaitingLock) END;
TransferLock(hdr, t)
END;
ELSE
TransferLock(hdr, c);
t := NIL
END;
ELSE
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
HALT(2204)
END;
Machine.Acquire(Machine.Objects);
IF c # NIL THEN Enter(c) END;
IF t # NIL THEN Enter(t) END;
IF StrongChecks THEN ASSERT(r.waitingOn = NIL) END;
r.condition := cond; r.condFP := slink;
r.waitingOn := obj; r.mode := AwaitingCond;
Put(hdr.awaitingCond, r);
Machine.ReleaseObject(hdr.locked);
Machine.ReleasePreemption;
SwitchToNew;
IF StrongChecks THEN
ASSERT(cond(slink));
ASSERT(hdr.lockedBy = r)
END
END AwaitNoPriorityInv;
PROCEDURE AwaitPriorityInv(cond: Condition; slink: SYSTEM.ADDRESS; obj: ProtectedObject; flags: SET);
VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r, c, t: Process; id, maxWaitingPrio, prevMaxWaitingPrio: LONGINT;
BEGIN
IF Stats THEN Machine.AtomicInc(Nawait) END;
IF 1 IN flags THEN
IF Stats THEN Machine.AtomicInc(NawaitNoIF) END;
IF cond(slink) THEN
IF Stats THEN Machine.AtomicInc(NawaitTrue) END;
RETURN
END
END;
SYSTEM.GET(SYSTEM.VAL(SYSTEM.ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
IF StrongChecks THEN
ASSERT(hdr IS Heaps.ProtRecBlock)
END;
Machine.Acquire(Machine.Objects);
id := Machine.ID();
r := running[id];
IF hdr.lockedBy = r THEN
IF StrongChecks THEN ASSERT(hdr.count = -1) END;
maxWaitingPrio := MaxPrio(hdr.waitingPriorities);
DEC(r.prioRequests[maxWaitingPrio]);
r.priority := MaxPrio(r.prioRequests);
IF hdr.awaitingCond.head # NIL THEN
c := FindCondition(hdr.awaitingCond)
ELSE
c := NIL
END;
IF c = NIL THEN
GetMaxPrio(hdr.awaitingLock, t);
IF t = NIL THEN
hdr.count := 0; hdr.lockedBy := NIL;
ELSE
IF StrongChecks THEN ASSERT(t.mode = AwaitingLock) END;
TransferLock(hdr, t);
END;
ELSE
TransferLock(hdr, c);
t := NIL;
END;
ELSE
Machine.Release(Machine.Objects);
HALT(2204)
END;
IF c # NIL THEN Enter(c) END;
IF t # NIL THEN Enter(t) END;
IF StrongChecks THEN ASSERT(r.waitingOn = NIL) END;
r.condition := cond; r.condFP := slink;
r.waitingOn := obj; r.mode := AwaitingCond;
IF hdr.lockedBy # NIL THEN
prevMaxWaitingPrio := MaxPrio(hdr.waitingPriorities);
INC(hdr.waitingPriorities[r.priority]);
IF r.priority > prevMaxWaitingPrio THEN PropagatePrio(hdr, prevMaxWaitingPrio, r.priority) END;
ELSE
INC(hdr.waitingPriorities[r.priority])
END;
Put(hdr.awaitingCond, r);
SwitchToNew;
IF StrongChecks THEN
ASSERT(cond(slink));
ASSERT(hdr.lockedBy = r)
END
END AwaitPriorityInv;
PROCEDURE UpdateState;
VAR t: Process;
BEGIN
Machine.Acquire(Machine.Objects);
t := running[Machine.ID ()];
IF t # NIL THEN
t.state.PC := Machine.CurrentPC();
t.state.SP := Machine.CurrentSP();
t.state.BP := Machine.CurrentBP();
END;
Machine.Release(Machine.Objects)
END UpdateState;
PROCEDURE Start*;
VAR id: LONGINT; idle: Idle; new: Process;
BEGIN
id := Machine.ID ();
NEW(idle);
Machine.Acquire(Machine.Objects);
Get(ready.q[MinPriority], new);
ASSERT(~(Preempted IN new.flags));
Machine.Release(Machine.Objects);
running[id] := new;
new.mode := Running; new.procID := id;
IF Machine.SSESupport THEN Machine.SSERestoreMin(new.sseAdr)
ELSE Machine.FPURestoreMin(new.sse)
END;
Machine.JumpToUserLevel(new.state.BP)
END Start;
PROCEDURE Init;
VAR
lock: PROCEDURE (obj: ProtectedObject; exclusive: BOOLEAN);
unlock: PROCEDURE (obj: ProtectedObject; dummy: BOOLEAN);
await: PROCEDURE (cond: Condition; slink: SYSTEM.ADDRESS; obj: ProtectedObject; flags: SET);
create: PROCEDURE (body: Body; priority: LONGINT; flags: SET; obj: ProtectedObject);
i: LONGINT;
BEGIN
ProcessorHLT := NIL;
maxReady := High;
lowestAllowedPriority := Low;
gcBarrier := {};
FOR i := 0 TO Machine.MaxCPU - 1 DO running[i] := NIL END;
FOR i := 0 TO NumPriorities - 1 DO rootedProcesses[i] := NIL END;
nextProcessID := 0; Machine.ticks := 0;
traceProcess := NIL;
lock := Lock; unlock := Unlock; await := Await; create := CreateProcess;
Modules.kernelProc[3] := SYSTEM.VAL (SYSTEM.ADDRESS, create);
Modules.kernelProc[4] := SYSTEM.VAL (SYSTEM.ADDRESS, await);
Modules.kernelProc[6] := SYSTEM.VAL (SYSTEM.ADDRESS, lock);
Modules.kernelProc[7] := SYSTEM.VAL (SYSTEM.ADDRESS, unlock);
terminate := Terminate;
trap[0] := Halt;
trap[1] := HaltUnbreakable;
trapReturn[0] := HaltReturn;
trapReturn[1] := HaltUnbreakableReturn;
END Init;
PROCEDURE InitEventHandling;
VAR i: LONGINT; clock: Clock;
BEGIN
FOR i := 0 TO NumIRQ-1 DO
interrupt[i].root := NIL; interrupt[i].process := NIL
END;
NEW(event); event.next := event; event.prev := event;
event.trigger := Machine.ticks + MAX(LONGINT) DIV 2;
timer := NIL; NEW(clock);
END InitEventHandling;
PROCEDURE InitGCHandling;
VAR finalizerCaller: FinalizerCaller;
BEGIN
gcProcess := NIL; NEW(gcActivity);
finalizerProcess := NIL; NEW(finalizerCaller);
END InitGCHandling;
PROCEDURE InitStats;
BEGIN
Nlock := 0; Nunlock := 0; Nawait := 0; NawaitNoIF := 0; NawaitTrue := 0;
Ncreate := 0; Nterminate := 0; Ncondition := 0; Ncondition1True := 0;
Ncondition2 := 0; Ncondition2True := 0;
Ntimeslice := 0; NtimesliceTaken := 0; NtimesliceNothing := 0;
NtimesliceIdle := 0; NtimesliceKernel := 0; NtimesliceV86 := 0; NtimesliceCritical := 0;
Npreempt := 0; NpreemptTaken := 0; NpreemptNothing := 0;
NpreemptKernel := 0; NpreemptV86 := 0; NpreemptCritical := 0;
Nenter := 0;
END InitStats;
PROCEDURE GCStatusFactory(): Heaps.GCStatus;
VAR gcStatusExt : GCStatusExt;
BEGIN
ASSERT(Heaps.gcStatus = NIL);
NEW(gcStatusExt);
RETURN gcStatusExt
END GCStatusFactory;
BEGIN
IF Stats THEN InitStats; END;
Init;
Machine.UpdateState;
Heaps.CollectGarbage(Modules.root);
NEW(ready);
Machine.InitInterrupts;
Machine.Start;
InitEventHandling;
InitGCHandling;
Heaps.gcStatus := GCStatusFactory();
entry := Machine.CurrentBP ();
SYSTEM.GET (entry+AddressSize, entry);
NewProcess(SYSTEM.VAL (Body, entry), {Resistant}, NIL, init);
init.priority := High;
init.staticPriority := init.priority;
FOR i := 0 TO LEN(init.prioRequests) - 1 DO init.prioRequests[i] := 0 END;
INC(init.prioRequests[init.priority]);
Machine.Acquire(Machine.Objects);
init.id := -1; Enter(init); init := NIL;
Machine.Release(Machine.Objects);
Start
END Objects.
(*
24.03.1998 pjm Started
06.05.1998 pjm CreateProcess init process, page fault handler
06.08.1998 pjm Moved exception interrupt handling here for current process
17.08.1998 pjm FindRoots method
02.10.1998 pjm Idle process
06.11.1998 pjm snapshot
25.03.1999 pjm Scope removed
28.05.1999 pjm EventHandler object
01.06.1999 pjm Fixed InterruptProcess lock error
16.06.1999 pjm Flat IRQ priority model to avoid GC deadlock
23.06.1999 pjm Flat IRQ priority experiment failed, rather do STI in FieldIRQ to avoid GC deadlock
29.06.1999 pjm Timeout in EventHandler object
13.01.2000 pjm Overed (Interrupt Objects, Event Handlers, Process ID, Process state, Process mode, Process stack, Await)
17.10.2000 pjm Priorities
22.10.2003 mib SSE2 extension
24.10.2003 phk Priority inversion / cycle counters
19.06.2007 ug Garbage Collector using meta data for stack inspection
*)
(*
Location Stack
Lock Current process
SwitchTo.A Current process
SwitchTo.B
*)