123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869 |
- MODULE Objects; (** AUTHOR "pjm"; PURPOSE "Active object runtime support"; *)
- IMPORT SYSTEM, Trace, Machine, Heaps, Modules;
- CONST
- (** Process flags *)
- Restart* = 0; (* Restart/Destroy process on exception (hardcoded in compiler (OPC.CallRecBody / PCC.SysStart)) *)
- PleaseHalt* = 10; (* Process requested to Halt itself soon *)
- Unbreakable*= 11; (* FINALLY shall not catch HALT exception (PleaseHalt is also set) *)
- SelfTermination*=12; (* Indicates the process has requested to terminate ifself (PleaseHalt is also set) *)
- Preempted* = 27; (* Has been preempted. *)
- Resistant* = 28; (* Can only be destroyed by itself *)
- (** Process modes *)
- Unknown* = 0; Ready* = 1; Running* = 2; AwaitingLock* = 3;
- AwaitingCond* = 4; AwaitingEvent* = 5; Suspended* = 6; (* Suspened for compatibility with WinAos, not used for native A2 *)
- Terminated* = 7;
- (** Process priorities *)
- MinPriority = 0; (* only system idle processes run at this priority level *)
- Low* = 1; Normal* = 2; High* = 3; (* "user" priorities *)
- GCPriority* = 4; (* priority of garbage collector *)
- Realtime* = 5; (* reserved for interrupt handling and realtime apps, these processes are not allowed to allocate memory *)
- NumPriorities = Heaps.NumPriorities; (* number of priority levels *)
- (* Process termination halt codes *)
- halt* = 2222;
- haltUnbreakable* = 2223;
- MinIRQ = Machine.IRQ0;
- NumIRQ = Machine.MaxIRQ-MinIRQ+1;
- Stats* = FALSE; (* maintain statistical counters *)
- TraceVerbose = FALSE; (* write out verbose trace info *)
- StrongChecks = TRUE; (* strong sanity checks *)
- VeryConservative = FALSE; (* temp - be very conservative about stack-based pointers *)
- YieldTrick = FALSE; (* avoid yield when no ready process available *)
- HandlePriorityInv = TRUE; (* enables or disables priority inversion handling. Handling of priority inversion leads to a simplified locking, see Lock, Unlock and Await *)
- (* constant used in GC Process.FindPointers *)
- InitDiff = MAX(LONGINT);
- AddressSize = SIZEOF(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; (* protected object *)
- ProcessQueue = Heaps.ProcessQueue;
- Body = PROCEDURE (self: ProtectedObject);
- Condition = PROCEDURE (slink: ADDRESS): BOOLEAN;
- InterruptList = POINTER TO RECORD
- next: InterruptList;
- handler: EventHandler
- END;
- TYPE
- (** All exported fields and variables should be considered read-only. *)
- Process* = OBJECT (Heaps.ProcessLink)
- VAR
- rootedNext : Process; (** for rootedProcesses *)
- obj-: ProtectedObject; (** associated active object *)
- state-: Machine.State; (** processor state of suspended process *)
- sse: Machine.SSEState; (* fpu and sse state of preempted process (only valid if Preempted IN flag) *)
- sseAdr: ADDRESS;
- condition-: Condition; (** awaited process' condition *)
- condFP-: ADDRESS; (** awaited process' condition's context *)
- mode-: LONGINT; (** process state *) (* only changed inside Objects lock ??? *)
- procID-: LONGINT; (** processor ID where running *)
- waitingOn-: ProtectedObject; (** obj this process is waiting on (for lock or condition) *)
- id-: LONGINT; (** unique process ID for tracing *)
- flags*: SET; (** process flags *)
- priority-, staticPriority*: WORD; (** process dynamic priority (can change during priority inversion handling) and static priority *) (* exported for AosExceptions *)
- stack*: Machine.Stack; (** user-level stack of process *)
- restartPC-: ADDRESS; (** entry point of body, for SAFE exception recovery *)
- restartSP-: ADDRESS; (** stack level at start of body, for SAFE exception recovery *)
- exp*: Machine.ExceptionState;
- oldReturnPC: ADDRESS;
- cpuCycles, lastCpuCycles : CpuCyclesArray;
- prioRequests : ARRAY NumPriorities OF WORD; (* priorities of processes that wait for resources locked by this process, only the highest priority per resource is stored *)
- context: ANY;
- (* set priority of process: Machine.Objects lock is taken *)
- PROCEDURE SetPriority(p : WORD);
- BEGIN
- DEC(prioRequests[staticPriority]);
- staticPriority := p;
- INC(prioRequests[staticPriority]);
- priority := MaxPrio(prioRequests)
- END SetPriority;
- PROCEDURE FindRoots; (* override *)
- VAR pc, bp, curbp, sp: ADDRESS; d0, d1: SIZE; first : BOOLEAN;
- BEGIN
- IF traceProcess # NIL THEN traceProcess(SELF) END;
- (* stack garbage collection *)
- IF (priority >= Low) & (priority <= High) & (mode >= Ready) & (mode # Terminated) THEN
- (* only processes with priority < GCPriority are preempted during GC,
- only those are allowed to allocate memory and their stacks are inspected.
- Furthermore, the process must be in a valid state, e.g. terminated processes have a disposed stack. *)
- IF Heaps.GCType = Heaps.HeuristicStackInspectionGC THEN
- IF VeryConservative THEN
- Heaps.RegisterCandidates(stack.adr, stack.high-stack.adr)
- ELSE
- sp := state.SP; (* cf. Enter *)
- 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 (* process is running already *)
- 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
- (* situation where pc and bp are not synchronized: *)
- (* entry protocol of a procedure:
- PUSH EBP -- 1 byte instruction length, if pc points to this instruction at offset 0 from the codeoffset then bp still refers to caller frame -> critical
- MOV EBP, ESP -- 2 bytes instruction length, do. for offset 1 from the codeoffset
- (followed by initialization of local variables)
- exit protocol of a procedure:
- MOV ESP, EBP -- 2 bytes instruction length
- POP EBP -- 1 byte instruction length
- RET n -- 3 bytes instruction length, if pc points to this instruction at offset 3 from the last statement then bp already refers to caller's frame -> critical
- *)
- IF (d0 = 0) OR (d1 = 3) THEN
- SYSTEM.GET(state.SP, pc); (* matching pc is at position of stack pointer *)
- ELSE
- SYSTEM.GET(state.SP+AddressSize, pc); (* matching pc is at 4 bytes after stack pointer, pushed base pointer is at stack pointer position *)
- END;
- ELSE
- (* regular case: bp and pc were synchronized *)
- curbp := bp;
- SYSTEM.GET(curbp, bp);
- SYSTEM.GET(curbp+AddressSize, pc);
- END;
- first := FALSE;
- ELSE
- (* regular case: bp and pc were synchronized *)
- curbp := bp;
- SYSTEM.GET(curbp, bp);
- SYSTEM.GET(curbp+AddressSize, pc);
- END
- END
- END
- ELSE
- HALT(900) (* wrong GCType constant *)
- END
- END
- END FindRoots;
- PROCEDURE FindPointers(bp, pc : ADDRESS; VAR diff0, diff1: SIZE);
- (*VAR data: Modules.ProcTableEntry; startIndex, i: LONGINT; ptr : 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; (* override *)
- VAR i: LONGINT;
- BEGIN
- (* only mark queues of user processes since these will not change during GC *)
- 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;
- (* called from Heaps.InvokeGC, i.e. this is a hidden upcall. However, it is necessary to take the Machine.Objects lock here since writing
- the set of variables here must not be interrupted, i.e. atomic writing of the set of variables is absolutely necessary. They system may hang
- if the lock is not taken. *)
- 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 (* this method cannot schedule the running user process with priority Low, Normal or High since
- lowestAllowedPriority is set to GCPriority *)
- ELSE
- Machine.Acquire(Machine.Objects);
- gcOngoing := FALSE;
- lowestAllowedPriority := Low;
- Machine.Release(Machine.Objects)
- END;
- END SetgcOngoing;
- (* caller must hold Machine.Objects lock *)
- 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 (* base type for collection, extended in Kernel.Mod *)
- PROCEDURE RemoveAll*(obj: ANY); (** abstract *)
- 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) (* to do: like Timer *)
- VAR interruptNumber: LONGINT;
- END Interrupter;
- VAR
- ready: ReadyProcesses; (* ready queue represented as an object that contains the queues *)
- maxReady: WORD; (* for all i : MinPriority <= maxReady < i < NumPriorities : Empty(ready.q[i]) *)
- lowestAllowedPriority: WORD; (* denotes the minimal user or realtime priority greater than the idle priority that can be
- scheduled depending on the GC status, minPriority = Low if GC is not running,
- minPrioriy = GCPriority otherwise *)
- running-{UNTRACED}: ARRAY Machine.MaxCPU OF Process; (** processes currently running, exported for Traps, not traced by the GC since it may change during collection *)
- nextProcessID: LONGINT;
- gcBarrier: SET; (* barrier that must be passed by all processors before actual collection starts *)
- gcActivity: GCActivity; (* active object for GC handling *)
- gcProcess: Process; (* suspended GC process, is NIL when collection has started, not equal NIL when no garbage collection is in process, same behaviour as for timer *)
- finalizerProcess: Process; (* finalizer process, regarded as part of GC *)
- interrupt: ARRAY NumIRQ OF RECORD
- root: InterruptList;
- process: Process
- END;
- processingIRQ: ARRAY NumIRQ OF BOOLEAN;
- rootedProcesses: ARRAY NumPriorities OF Process; (* list of potential processes that are not traced by GC when processing the ready queues, since GC only traces processes with
- priorities Low ... High in ready queues. The potentially not traced processes are rooted here and traced by the GC *)
- initObject: ProtectedObject; (* Active object for the init process *)
- event: Timer; (* list of events *)
- timer (*, realtimeTimer *): Process; (* suspended timer processes *)
- terminate: PROCEDURE;
- trap, trapReturn: ARRAY 2 OF PROCEDURE;
- ProcessorHLT*: PROCEDURE; (** installable procedure to halt the current processor while idle *)
- traceProcess*: TraceProcess; (** for debugging purposes (see Info.Active) *)
- entry: ADDRESS;
- init: Process;
- (* Performance monitoring *)
- idlecount*: ARRAY Machine.MaxCPU OF LONGINT; (** count of idle process timeslice interrupts *)
- idleCycles- : ARRAY Machine.MaxCPU OF HUGEINT; (** CPU cycles of idle threads *)
- perfTsc: ARRAY Machine.MaxCPU OF HUGEINT;
- (* Statistics *)
- 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 : WORD;
- BEGIN
- ASSERT(new = NIL);
- t := queue.head;
- maxPriority := MIN(WORD);
- 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 (* zero elements in queue *)
- (* skip *)
- ELSE (* more than one element in queue *)
- 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;
- (* Get a process from a queue (NIL if none). Caller must hold lock for specific queue. *)
- PROCEDURE Get(VAR queue: ProcessQueue; VAR new: Process);
- VAR t: Heaps.ProcessLink;
- BEGIN
- t := queue.head;
- IF t = NIL THEN (* zero elements in queue *)
- (* skip *)
- ELSIF t = queue.tail THEN (* one element in queue *)
- queue.head := NIL; queue.tail := NIL (* {(t.next = NIL) & (t.prev = NIL)} *)
- ELSE (* more than one element in queue *)
- queue.head := t.next; t.next := NIL; queue.head.prev := NIL
- END;
- ASSERT((t = NIL) OR (t.next = NIL) & (t.prev = NIL)); (* temp strong check *)
- IF t = NIL THEN
- new := NIL
- ELSE
- ASSERT(t IS Process);
- new := t(Process)
- END;
- END Get;
- (* Put a process in a queue. Caller must hold lock for specific queue. *)
- (* If t was running, be careful to protect Put and the subsequent SwitchTo with the ready lock. *)
- PROCEDURE Put(VAR queue: ProcessQueue; t: Process);
- BEGIN (* {t # NIL & t.next = NIL} *)
- ASSERT((t.next = NIL) & (t.prev = NIL));
- IF queue.head = NIL THEN (* queue empty *)
- queue.head := t
- ELSE (* queue not empty *)
- queue.tail.next := t; t.prev := queue.tail
- END;
- queue.tail := t
- END Put;
- (* Select a process of at least the specified priority to run next on current processor (returns NIL if none). Caller must hold ready lock. *)
- PROCEDURE Select(VAR new: Process; priority: WORD);
- VAR thresholdPrio: WORD;
- 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;
- (* Enter a process in the ready queue. Caller must hold ready lock. *)
- (* If t was running, be careful to make Enter and the subsequent SwitchTo atomic, as the process could be grabbed by another process while it is still running. *)
- 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 (* to do: re-establish global priority invariant *)
- END
- END Enter;
- (* Remove a process from a queue that contains it. Caller must hold lock for specific queue. *)
- (* Not intended for frequent use. *)
- 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;
- (* Switch to the specified process. Caller must hold ready lock. Return may be on different processor! *)
- PROCEDURE SwitchTo(VAR running: Process; new: Process); (* parameters used in SwitchToState, TerminateThis, New *)
- VAR id: LONGINT;
- BEGIN
- ASSERT(Machine.CS () MOD 4 = Machine.UserLevel); (* registers hold user state *)
- id := Machine.ID ();
- INC (running.cpuCycles[id], Machine.GetTimer () - perfTsc[id]);
- IF running.priority = MinPriority THEN (* Special treatment for idle threads *)
- INC (idleCycles[id], Machine.GetTimer () - perfTsc[id]);
- END;
- (* save current state *)
- running.state.PC := Machine.CurrentPC (); (* for GC *) (* ug *)
- running.state.SP := SYSTEM.GetStackPointer (); (* for GC *)
- running.state.BP := SYSTEM.GetFramePointer (); (* save state *)
- 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); (* switching to user mode *)
- EXCL(new.flags, Preempted);
- perfTsc[id] := Machine.GetTimer ();
- SYSTEM.SetStackPointer (new.state.SP); (* for UpdateState - run on new stack (EBP on old) *)
- Machine.PushState(new.state);
- IF Machine.SSESupport THEN Machine.SSERestoreFull(new.sseAdr)
- ELSE Machine.FPURestoreFull(new.sse)
- END;
- Machine.Release(Machine.Objects);
- Machine.JumpState (* pops the state parameter from the stack and returns from interrupt *)
- ELSE
- IF Machine.SSESupport THEN Machine.SSERestoreMin(new.sseAdr)
- ELSE Machine.FPURestoreMin(new.sse)
- END;
- perfTsc[id] := Machine.GetTimer ();
- SYSTEM.SetStackPointer (new.state.SP); (* run on new stack *)
- SYSTEM.SetFramePointer (new.state.BP);
- Machine.Release(Machine.Objects);
- END;
- (*
- MOV ESP, EBP ; exit code generated by compiler
- POP EBP
- RET 8
- *)
- END SwitchTo;
- (* Select a new process to run and switch to it. Caller must hold ready lock. *)
- PROCEDURE SwitchToNew;
- VAR new: Process;
- BEGIN
- Select(new, MinPriority); (* will return at least an Idle process *)
- new.procID := Machine.ID ();
- SwitchTo(running[new.procID], new)
- END SwitchToNew;
- (** Relinquish control. *)
- 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 (* found another process *)
- Enter(r);
- new.procID := Machine.ID ();
- SwitchTo(running[new.procID], new)
- ELSE (* stay with same process *)
- Machine.Release(Machine.Objects)
- END
- END
- END Yield;
- PROCEDURE SwitchToState(new: Process; VAR state: Machine.State);
- BEGIN
- (* simulate return from SwitchTo - MOV ESP, EBP; POP EBP; RET 8 *)
- state.SP := new.state.BP+AddressSize*2; (* AddressSize*2 is effect of POP, RET *)
- SYSTEM.GET (new.state.BP, state.BP); (* effect of POP *)
- SYSTEM.GET (new.state.BP + AddressSize, state.PC); (* effect of RET *)
- END SwitchToState;
- (** Preempt the current process. *)
- PROCEDURE Timeslice*(VAR state: Machine.State);
- VAR id: LONGINT; new: Process;
- BEGIN
- (* handle a timer tick *)
- Machine.Acquire(Machine.Objects);
- IF Stats THEN Machine.AtomicInc(Ntimeslice) END;
- id := Machine.ID ();
- IF id = 0 THEN (* process 0 checks event queues *)
- IF event.next.trigger - Machine.ticks <= 0 THEN (* next normal event due *)
- IF event.next # event THEN (* not dummy event *)
- IF timer # NIL THEN
- ASSERT(timer.mode = AwaitingEvent);
- Enter(timer); timer := NIL
- END
- ELSE (* reset dummy event *)
- event.trigger := Machine.ticks + MAX(LONGINT) DIV 2 (* ignore overflow *)
- 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;
- (* pre-empt the current process *)
- IF Machine.PreemptCount(id) = 1 THEN (* check against 1, because we are holding one lock *)
- IF ~(Machine.VMBit IN state.FLAGS) THEN (* not V86 mode *)
- IF state.CS MOD 4 = Machine.UserLevel THEN (* not kernel mode (used during initialization or interrupts) *)
- IF running[id].priority # MinPriority THEN (* idle processes are not timesliced *)
- Select(new, running[id].priority);
- IF new # NIL THEN
- ASSERT(Machine.CS () MOD 4 = Machine.KernelLevel); (* otherwise we can not change state.SP *)
- 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); (* to do: floating-point exception possible / Machine.SetupFPU *)
- 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;
- (* Check if the process has the PleasHalt flag and handle it. *)
- IF PleaseHalt IN running[id].flags THEN
- (* Simulate procedure call: Increase stack & put return PC *)
- DEC(state.SP, AddressSize);
- SYSTEM.PUT (state.SP, state.PC); (* Here an stack overflow could happen! *)
- (* Set the right halt procedure *)
- IF (Unbreakable IN running[id].flags) THEN
- state.PC := SYSTEM.VAL (ADDRESS, trap[1]);
- ELSE
- state.PC := SYSTEM.VAL (ADDRESS, trap[0]);
- END;
- END;
- ELSE
- INC(idlecount[id]);
- IF Stats THEN Machine.AtomicInc(NtimesliceIdle) END;
- END
- ELSE
- (* can not interrupt kernel mode, because SwitchTo would not switch back to it *)
- IF Stats THEN Machine.AtomicInc(NtimesliceKernel) END (* kernel mode, e.g. during page fault or FieldIRQ *)
- END
- ELSE
- IF Stats THEN Machine.AtomicInc(NtimesliceV86) END (* V86 mode *)
- END
- ELSE
- IF Stats THEN Machine.AtomicInc(NtimesliceCritical) END (* not preemptable *)
- END;
- Machine.Release(Machine.Objects)
- END Timeslice;
- (** Return current process. (DEPRECATED, use ActiveObject) *)
- PROCEDURE CurrentProcess*( ): Process;
- BEGIN
- RETURN SYSTEM.VAL(Process, Machine.GetProcessPtr());
- END CurrentProcess;
-
- PROCEDURE CurrentContext*(): ANY;
- VAR p: Process;
- BEGIN
- p := CurrentProcess();
- IF p # NIL THEN RETURN p.context
- ELSE RETURN NIL
- END;
- END CurrentContext;
- PROCEDURE SetContext*(context: ANY);
- VAR p: Process;
- BEGIN
- p := CurrentProcess();
- IF p # NIL THEN p.context := context END;
- END SetContext;
- (* Return stack bottom of process. For compatibility WinAos/UnixAos/NativeAos *)
- PROCEDURE GetStackBottom*(p: Process): ADDRESS;
- BEGIN
- RETURN p.stack.high
- END GetStackBottom;
- (** Return the active object currently executing. *)
- PROCEDURE ActiveObject* (): ANY;
- VAR r: Process;
- BEGIN
- r := SYSTEM.VAL(Process, Machine.GetProcessPtr ());
- RETURN r.obj
- END ActiveObject;
- (** Return the ID of the active currently executing process. *)
- PROCEDURE GetProcessID* (): LONGINT;
- VAR r: Process;
- BEGIN
- r := SYSTEM.VAL (Process, Machine.GetProcessPtr ());
- RETURN r.id
- END GetProcessID;
- (** Set the current process' priority. *)
- PROCEDURE SetPriority*(priority: WORD);
- VAR id: LONGINT;
- BEGIN
- ASSERT((priority >= Low) & (priority <= Realtime)); (* priority in bounds *)
- 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
- (* to do: re-establish global priority invariant *)
- END
- END SetPriority;
- (** Return TRUE iff the specified protected object is locked exclusive to the current process. *)
- PROCEDURE LockedByCurrent*(obj: ANY): BOOLEAN;
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; id: LONGINT; res: BOOLEAN;
- BEGIN
- SYSTEM.GET(SYSTEM.VAL(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;
- (** Return number of ready and running processes, excluding idle processes. *)
- 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;
- (** Return number of CPU cycles consumed by the specified process for each processor. If all is TRUE,
- return the number of cycles since the process has been created. If FALSE, return the number of cycles
- consumed since the last time asked. *)
- 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]; (* actually could have changed meanwhile *)
- END;
- END;
- END GetCpuCycles;
- PROCEDURE CurrentProcessTime*(): HUGEINT;
- VAR result: HUGEINT; process: Process; i: LONGINT;
- BEGIN
- process := CurrentProcess();
- FOR i := 0 TO Machine.MaxCPU-1 DO result := result + process.cpuCycles[i]; END;
- RETURN result;
- END CurrentProcessTime;
- PROCEDURE TimerFrequency*(): HUGEINT;
- BEGIN
- RETURN 1000000000;
- END TimerFrequency;
- (* Handle hardware interrupt and route it to an interrupt handler process. *)
- PROCEDURE FieldIRQ(VAR state: Machine.State);
- VAR t: Process; id: LONGINT; new: Process; preempt: BOOLEAN;
- BEGIN
- Machine.DisableIRQ(state.INT); (* do this before acknowledging irq *)
- IF StrongChecks THEN
- IF processingIRQ[state.INT-MinIRQ] THEN
- Trace.String("IRQ recursion "); Trace.Int(state.INT,1); Trace.Ln;
- RETURN
- ELSE
- processingIRQ[state.INT-MinIRQ] := TRUE;
- END;
- END;
-
- (* if the reenabling of interrupts cannot be circumvented, then it is REQUIRED to acknowledge interrupts
- BEFORE reenabling. Otherwise spurious IRQs cannot be identified as such.
- Please note that this particular problem with spurious IRQs cannot observed on many machines but IF it is observed
- then the machine will behave unexpected. Very hard to debug and trace!
- Machine.Ack(state.INT);
- Machine.Sti (); (* avoid Processors.StopAll deadlock when waiting for locks below (remove this) *)
- *)
-
- 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;
- (* pre-empt the current process *)
- IF Machine.PreemptCount(id) = 1 THEN (* check against 1, because we are holding one lock *)
- IF ~(Machine.VMBit IN state.FLAGS) THEN (* not V86 mode *)
- IF state.CS MOD 4 = Machine.UserLevel THEN (* not kernel mode (used during initialization or interrupts) *)
- Select(new, running[id].priority + 1);
- IF new # NIL THEN
- ASSERT(Machine.CS () MOD 4 = Machine.KernelLevel); (* otherwise we can not change state.SP *)
- INC (running[id].cpuCycles[id], Machine.GetTimer () - perfTsc[id]);
- IF running[id].priority = MinPriority THEN (* Special treatment for idle threads *)
- 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); (* to do: floating-point exception possible / Machine.SetupFPU *)
- 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
- (* can not interrupt kernel mode, because SwitchTo would not switch back to it *)
- IF Stats THEN Machine.AtomicInc(NpreemptKernel) END (* kernel mode, e.g. during page fault or FieldIRQ *)
- END
- ELSE
- IF Stats THEN Machine.AtomicInc(NpreemptV86) END (* V86 mode *)
- END
- ELSE
- IF Stats THEN Machine.AtomicInc(NpreemptCritical) END (* not preemptable *)
- END
- END;
- Machine.Release(Machine.Objects)
- END FieldIRQ;
- (* Process scheduled to handle an interrupt. *)
- 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; (* concurrent updates allowed in InstallHandler and RemoveHandler *)
- WHILE h # NIL DO h.handler (); h := h.next END;
- Machine.Acquire(Machine.Objects);
- ASSERT(running[Machine.ID ()] = t); (* strong check *)
- t.mode := AwaitingEvent;
- processingIRQ[int-MinIRQ] := FALSE;
- Machine.EnableIRQ(int);
- SwitchToNew
- END
- END InterruptProcess;
- (** Install interrupt handler. *)
- PROCEDURE InstallHandler*(h: EventHandler; int: LONGINT);
- VAR t: Process; new: BOOLEAN; ih: Interrupter; n: InterruptList; i: LONGINT;
- BEGIN
- ASSERT((int >= MinIRQ) & (int-MinIRQ < NumIRQ)); (* range check *)
- IF interrupt[int-MinIRQ].process = NIL THEN (* first handler for this irq *)
- (* allocate process outside lock region, to avoid GC lock problems. *)
- (* hack: use type parameter to pass int index & set obj to h, for System.ShowProcesses *)
- NEW(ih); ih.interruptNumber := int;
- NewProcess(InterruptProcess, {Resistant}, ih, t);
- t.priority := High; (* second-level interrupt handling processes have high priority, handlers may allocate memory, use exclusive locks and awaits *)
- 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 (* still first handler for this irq *)
- 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; (* can be concurrent with loop in InterruptProcess *)
- interrupt[int-MinIRQ].root := n;
- Machine.Release(Machine.Objects);
- IF new THEN Machine.InstallHandler(FieldIRQ, int) END (* do outside lock region to avoid NEW/GC deadlock *)
- END InstallHandler;
- (** Remove interrupt handler. *)
- PROCEDURE RemoveHandler*(h: EventHandler; int: LONGINT);
- VAR p, c: InterruptList;
- BEGIN
- ASSERT((int >= MinIRQ) & (int-MinIRQ < NumIRQ)); (* range check *)
- 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 (* handler found *)
- IF p = NIL THEN
- interrupt[int-MinIRQ].root := c.next;
- (*
- IF c.inext = NIL THEN (* this was the last handler *)
- Machine.RemoveHandler(FieldIRQ, int)
- (* to do: synchronize with FieldIRQ and InterruptProcess *)
- END
- *)
- ELSE
- p.next := c.next
- END
- ELSE
- HALT(99); (* handler not found *)
- END;
- (* can not clear c.next field, because InterruptProcess may be traversing it. *)
- Machine.Release(Machine.Objects)
- END RemoveHandler;
- (* local procedure *)
- PROCEDURE SetTimeoutAbsOrRel(t: Timer; h: EventHandler; ms: LONGINT; isAbsolute: BOOLEAN);
- VAR e: Timer; trigger: LONGINT;
- BEGIN
- ASSERT(Machine.Second= 1000); (* assume milliseconds for now *)
- 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 (* ignore overflow *) END;
- IF t.next # NIL THEN (* cancel previous timeout *)
- t.next.prev := t.prev; t.prev.next := t.next
- END;
- t.trigger := trigger; t.handler := h;
- e := event.next; (* performance: linear search! *)
- 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;
- (** Set (or reset) an event handler object's timeout value. *)
- PROCEDURE SetTimeout*(t: Timer; h: EventHandler; ms: LONGINT);
- BEGIN
- SetTimeoutAbsOrRel(t, h, ms, FALSE)
- END SetTimeout;
- (** Set (or reset) an event handler object's timeout value. Here ms is absolute *)
- PROCEDURE SetTimeoutAt*(t: Timer; h: EventHandler; ms: LONGINT);
- BEGIN
- SetTimeoutAbsOrRel(t, h, ms, TRUE)
- END SetTimeoutAt;
- (** Cancel an event handler object's timeout, if any. It is possible that the timer has expired, but not yet been scheduled to run. *)
- PROCEDURE CancelTimeout*(t: Timer);
- BEGIN
- Machine.Acquire(Machine.Objects);
- ASSERT(t # event);
- IF t.next # NIL THEN
- t.next.prev := t.prev;
- IF t.prev #NIL THEN t.prev.next := t.next END;
- END;
- t.next := NIL; t.prev := NIL;
- Machine.Release(Machine.Objects)
- END CancelTimeout;
- (** Terminate the current process and switch to next process. *)
- PROCEDURE Terminate*; (* exported for Linker *)
- VAR id: LONGINT;
- BEGIN
- IF Stats THEN Machine.AtomicInc(Nterminate) END;
- Machine.Acquire(Machine.Objects);
- id := Machine.ID ();
- (*running[id].state.PC := CallerPC ();*) (* for tracing *)
- running[id].mode := Terminated; (* a process can also be "terminated" if the queue containing it is garbage collected *)
- SwitchToNew;
- HALT(2201) (* process resurrected *)
- END Terminate;
- PROCEDURE Halt;
- BEGIN
- HALT(halt); (* process halted *)
- END Halt;
- PROCEDURE HaltUnbreakable;
- BEGIN
- HALT(haltUnbreakable); (* process halted *)
- END HaltUnbreakable;
- (* Set the return PC which is saved in the process and set it to -1 *)
- PROCEDURE HaltAltPC(haltCode: WORD);
- VAR bp: ADDRESS; p: Process;
- BEGIN
- p := running[Machine.ID ()];
- ASSERT(p.oldReturnPC # -1);
- bp := SYSTEM.GetFramePointer ();
- SYSTEM.PUT (bp + AddressSize, p.oldReturnPC);
- CASE haltCode OF
- |halt: HALT(halt);
- |haltUnbreakable: HALT(haltUnbreakable);
- END;
- END HaltAltPC;
- PROCEDURE HaltReturn;
- VAR bp: ADDRESS;
- BEGIN
- bp := SYSTEM.GetFramePointer ();
- SYSTEM.GET (bp, bp); (* Get the dynamic link *)
- SYSTEM.SetFramePointer (bp); (* Undo the actual paf *)
- HaltAltPC(halt);
- END HaltReturn;
- PROCEDURE HaltUnbreakableReturn;
- VAR bp: ADDRESS;
- BEGIN
- bp := SYSTEM.GetFramePointer ();
- SYSTEM.GET (bp, bp); (* Get the dynamic link *)
- SYSTEM.SetFramePointer (bp); (* Undo the actual paf *)
- HaltAltPC(haltUnbreakable);
- END HaltUnbreakableReturn;
- PROCEDURE TerminateThis*(t: Process; unbreakable: BOOLEAN);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; pc, fp : ADDRESS;
- (* terminates a process that is either in mode AwaitingLock or AwaitingCond *)
- PROCEDURE TerminateAwaiting(t: Process);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock;
- BEGIN
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, t.waitingOn) + Heaps.HeapBlockOffset, hdr);
- ASSERT(hdr IS Heaps.ProtRecBlock);
- IF t.mode = AwaitingLock THEN
- fp := t.state.BP; (* SwitchTo PAF *)
- SYSTEM.GET (fp, fp); (* SwitchToNew PAF *)
- SYSTEM.GET (fp, fp); (* Lock PAF*)
- SYSTEM.GET (fp + AddressSize, pc); (* Get the return address*)
- 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 (ADDRESS, trapReturn[1]))
- ELSE
- SYSTEM.PUT (t.state.BP + AddressSize, SYSTEM.VAL (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 (ADDRESS, trapReturn[1]))
- ELSE
- SYSTEM.PUT (t.state.BP + AddressSize, SYSTEM.VAL (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 (ADDRESS, trap[1])
- ELSE t.state.PC := SYSTEM.VAL (ADDRESS, trap[0]) END
- |AwaitingLock, AwaitingCond:
- IF HandlePriorityInv THEN
- TerminateAwaiting(t)
- ELSE
- SYSTEM.GET(SYSTEM.VAL(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: (* skip *)
- END;
- Machine.Release(Machine.Objects)
- END
- END TerminateThis;
- (* called by WMProcessInfo to obtain the current state of a running process *)
- PROCEDURE UpdateProcessState*( p: Process );
- BEGIN
- (* update p.stat.{PC,BP,SP} *)
- END UpdateProcessState;
- (* Finalize a process. *)
- PROCEDURE FinalizeProcess(t: ANY);
- BEGIN
- Machine.DisposeStack(t(Process).stack)
- END FinalizeProcess;
- (* Allocate a new process associated with "obj". Must be outside lock region, because of potential GC. *)
- PROCEDURE NewProcess(body: Body; flags: SET; obj: ProtectedObject; VAR new: Process);
- VAR t: Process; sp: ADDRESS; id: LONGINT; fn: Heaps.FinalizerNode;
- BEGIN
- NEW(t); NEW(fn); (* implicit call Heaps.NewRec *)
- t.next := NIL; t.prev := NIL; t.rootedNext := NIL;
- t.waitingOn := NIL; t.flags := flags;
- t.obj := obj; t.mode := Unknown;
- (* initialize the stack *)
- Machine.NewStack(t.stack, t, sp);
- IF VeryConservative THEN
- Machine.Fill32(t.stack.adr, sp-t.stack.adr, LONGINT(0D0D0DEADH))
- END;
- SYSTEM.PUT (sp-1*AddressSize, obj); (* self parameter for body *)
- SYSTEM.PUT (sp-2*AddressSize, terminate); (* return address for body *)
- (* the following will be popped by SwitchTo exit code or Machine.JumpToUserLevel *)
- SYSTEM.PUT (sp-3*AddressSize, body); (* return address for SwitchTo (body entry point) *)
- SYSTEM.PUT (sp-4*AddressSize, NIL); (* end of dynamic link list (FP value at entry to body) *)
- t.sseAdr := ADDRESSOF(t.sse) + ((-ADDRESSOF(t.sse)) MOD 16);
- IF Machine.SSESupport THEN Machine.SSESaveMin(t.sseAdr)
- ELSE Machine.FPUSaveMin(t.sse) (* inherit FPU state of caller *)
- END;
- t.state.BP := sp-4*AddressSize;
- t.state.SP := t.state.BP;
- t.state.PC := 0; (* indicating that process is not running yet *)
- (* set up exception handling *)
- IF Restart IN flags THEN (* restart object body *)
- t.restartPC := SYSTEM.VAL (ADDRESS, body);
- t.restartSP := sp-2*AddressSize (* 1 parameter and return address of body *)
- ELSE (* terminate process *)
- t.restartPC := SYSTEM.VAL (ADDRESS, terminate);
- t.restartSP := sp
- END;
- fn.finalizer := FinalizeProcess;
- Heaps.AddFinalizer(t, fn);
- (* return *)
- FOR id := 0 TO Machine.MaxCPU-1 DO t.cpuCycles[id] := 0 END;
- new := t
- END NewProcess;
- (* Create the process associated with an active object (kernel call). *)
- PROCEDURE CreateProcess*(body: Body; priority: WORD; flags: SET; obj: ProtectedObject);
- VAR t: Process; type: ADDRESS; heapBlock {UNTRACED}: Heaps.HeapBlock; i: LONGINT;
- BEGIN
- IF Stats THEN Machine.AtomicInc(Ncreate) END;
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, heapBlock);
- ASSERT(heapBlock IS Heaps.ProtRecBlock); (* protected object *)
- SYSTEM.GET (SYSTEM.VAL (ADDRESS, obj) + Heaps.TypeDescOffset, type); (* type tag *)
- IF Restart IN flags THEN INCL(flags, Resistant) END; (* SAFE => Restart & Resistant *)
- NewProcess(body, flags, obj, t);
- Machine.Acquire(Machine.Objects);
- t.id := nextProcessID; INC(nextProcessID);
- t.context := CurrentContext();
- IF priority = 0 THEN (* no priority specified *)
- t.priority := running[Machine.ID ()].priority (* inherit priority of creator *)
- ELSIF priority > 0 THEN (* positive priority specified *)
- t.priority := priority
- ELSE (* negative priority specified (only for Idle process) *)
- 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 : (* do nothing, processes with this priority are traced by GC automatically *)
- | GCPriority, Realtime : t.rootedNext := rootedProcesses[t.priority]; rootedProcesses[t.priority] := t
- END;
- Enter(t);
- Machine.Release(Machine.Objects)
- END CreateProcess;
- (* Lock a protected object (kernel call) *)
- (* There are two different procedures for locking a protected object in case of priority inversion handling enabled or disabled due to the different
- locking strategy. *)
- PROCEDURE Lock*(obj: ProtectedObject; exclusive: BOOLEAN);
- BEGIN
- IF HandlePriorityInv THEN
- LockPriorityInv(obj, exclusive)
- ELSE
- LockNoPriorityInv(obj, exclusive)
- END
- END Lock;
- (* Lock a protected object if priority inversion handling is disabled. Header locks, preemption and Machine.Objects locks are used. *)
- PROCEDURE LockNoPriorityInv(obj: ProtectedObject; exclusive: BOOLEAN);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r: Process; id: LONGINT;
- BEGIN (* {called from user level} *)
- IF Stats THEN Machine.AtomicInc(Nlock) END;
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- IF StrongChecks THEN
- ASSERT(hdr IS Heaps.ProtRecBlock); (* protected object *)
- ASSERT(exclusive) (* shared not implemented yet *)
- END;
- id := Machine.AcquirePreemption ();
- Machine.AcquireObject(hdr.locked);
- IF hdr.count = 0 THEN (* not locked *)
- hdr.count := -1; hdr.lockedBy := SYSTEM.VAL (Process, Machine.GetProcessPtr ()); (* set exclusive lock *)
- Machine.ReleaseObject(hdr.locked);
- Machine.ReleasePreemption;
- ELSE (* locked *)
- r := SYSTEM.VAL (Process, Machine.GetProcessPtr ());
- IF hdr.lockedBy = r THEN
- Machine.ReleaseObject(hdr.locked);
- Machine.ReleasePreemption;
- ASSERT(hdr.lockedBy # r, 2203); (* nested locks not allowed *)
- 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;
- (*
- (* propagation of priorities - lock Machine.Objects is taken.
- This is a procedure that calls itself recursively if a higher priority is propagated along a chain of resources and processes where each resource
- is locked by a process that itself waits on a resource. The procedure can be rewritten into a non-recursive procedure if needed..
- Remark: parameters of type Heaps.HeapBlock or extensions of it are not passed as parameters for clarity and safety reasons .
- Instead, a ProtectedObject pointer is passed as the first parameter. *)
- PROCEDURE PropagatePrio(obj: ProtectedObject; prevMaxWaitingPrio, waitingPrio: LONGINT);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; p: Process;
- BEGIN
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- 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(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- prevMaxWaitingPrio := MaxPrio(hdr.waitingPriorities);
- DEC(hdr.waitingPriorities[p.priority]);
- INC(hdr.waitingPriorities[waitingPrio]);
- IF waitingPrio > prevMaxWaitingPrio THEN PropagatePrio(obj, prevMaxWaitingPrio, waitingPrio) END
- END;
- IF waitingPrio > p.priority THEN
- IF p.mode = Ready THEN Remove(ready.q[p.priority], p) END; (* remove p from the lower priority queue ... *)
- p.priority := waitingPrio;
- IF p.mode = Ready THEN Enter(p) END; (* ... and add it to the higher priority queue *)
- END
- END;
- END PropagatePrio;
- *)
- (* propagation of priorities - lock Machine.Objects is taken.
- This procedure is the iterative version of the above commented out recursive procedure.
- Remark: hdr is an actually UNTRACED parameter. The GC, however, can handle this, see procedure Heaps.Mark, there is a check whether the
- pointer to the header part is valid. In case of hdr, the pointer ot the header part is NIL. *)
- 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(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- prevMaxWaitingPrio := MaxPrio(hdr.waitingPriorities);
- DEC(hdr.waitingPriorities[p.priority]);
- INC(hdr.waitingPriorities[waitingPrio]);
- ELSE (* p is not waiting for a resource or waitingPrio is less or equal to p's priority - priority propagation finishes *)
- propagateFurther := FALSE
- END;
- IF waitingPrio > p.priority THEN (* independently of whether p is waiting on a resource or not the priority of p is changed if it is lower than waitingPrio *)
- IF p.mode = Ready THEN Remove(ready.q[p.priority], p) END; (* remove p from the lower priority queue ... *)
- p.priority := waitingPrio;
- IF p.mode = Ready THEN Enter(p) END; (* ... and add it to the higher priority queue *)
- END
- ELSE (* current resource is not locked - priority propagation finishes *)
- propagateFurther := FALSE
- END
- END
- END PropagatePrio;
- (* TO DO: adapt priority inversion algorithm such that priority of a process is not raised higher than High, it must not become Realtime, otherwise
- GC may be corrupted *)
- (* Lock a protected object if priority inversion handling is enabled. Machine.Objects lock is used. *)
- PROCEDURE LockPriorityInv(obj: ProtectedObject; exclusive: BOOLEAN);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r: Process;
- maxWaitingPrio, prevMaxWaitingPrio: LONGINT;
- BEGIN (* {called from user level} *)
- IF Stats THEN Machine.AtomicInc(Nlock) END;
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- IF StrongChecks THEN
- ASSERT(hdr IS Heaps.ProtRecBlock); (* protected object *)
- ASSERT(exclusive) (* shared not implemented yet *)
- END;
- Machine.Acquire(Machine.Objects);
- r := SYSTEM.VAL(Process, Machine.GetProcessPtr());
- IF hdr.count = 0 THEN (* not locked *)
- hdr.count := -1; hdr.lockedBy := r; (* set exclusive lock *)
- maxWaitingPrio := MaxPrio(hdr.waitingPriorities);
- INC(r.prioRequests[maxWaitingPrio]);
- r.priority := MaxPrio(r.prioRequests);
- Machine.Release(Machine.Objects);
- ELSE (* locked (to do: on multiprocessors, perhaps spin here for a while, if lockedBy.mode = running) *)
- IF hdr.lockedBy = r THEN
- Machine.Release(Machine.Objects);
- ASSERT(hdr.lockedBy # r, 2203); (* nested locks not allowed *)
- 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;
- (* Find the first true condition from the queue and remove it. Assume the object is currently locked. *)
- 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;
- (* Find highest priority in array of priority counts *)
- PROCEDURE MaxPrio(CONST priorityCounts: ARRAY OF WORD): WORD;
- VAR i: WORD;
- 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;
- (* Unlock a protected object (kernel call). *)
- (* There are two different procedures for locking a protected object in case of priority inverison handling enabled or disabled due to the different
- locking strategy. *)
- PROCEDURE Unlock*(obj: ProtectedObject; dummy: BOOLEAN);
- BEGIN
- IF HandlePriorityInv THEN
- UnlockPriorityInv(obj)
- ELSE
- UnlockNoPriorityInv(obj)
- END
- END Unlock;
- (* transfer the lock from a resource to another process.
- Remark: hdr is an actually UNTRACED parameter. The GC, however, can handle this, see procedure Heaps.Mark, there is a check whether the
- pointer to the header part is valid. In case of hdr, the pointer ot the header part is NIL. *)
- PROCEDURE TransferLock(hdr: Heaps.ProtRecBlock; p: Process);
- VAR maxWaitingPrio: WORD;
- 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;
- (* Unlock a protected object if priority inversion handling is disabled. Header locks, preemption and Machine.Objects locks are used. *)
- 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(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- IF StrongChecks THEN
- ASSERT(hdr IS Heaps.ProtRecBlock) (* protected object *)
- END;
- ASSERT(hdr.count = -1); (* exclusive locked *)
- IF hdr.awaitingCond.head # NIL THEN (* evaluate the waiting conditions *)
- (* we are holding the lock, so the queue can not change (to do: except in TerminateThis) *)
- c := FindCondition(hdr.awaitingCond) (* interrupts should be on during this call *)
- 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 (* no true condition found, check the lock queue *)
- 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 (* true condition found, transfer the lock *)
- 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;
- (* Unlock a protected object in case priority inversion handling is enabled. Machine.Objects lock is used. *)
- PROCEDURE UnlockPriorityInv(obj: ProtectedObject);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; t, c, r: Process; maxWaitingPrio: WORD;
- BEGIN
- IF Stats THEN Machine.AtomicInc(Nunlock) END;
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- IF StrongChecks THEN
- ASSERT(hdr IS Heaps.ProtRecBlock) (* protected object *)
- END;
- ASSERT(hdr.count = -1); (* exclusive locked *)
- IF hdr.awaitingCond.head # NIL THEN (* evaluate the waiting conditions *)
- (* we are holding the lock, so the queue can not change (to do: except in TerminateThis) *)
- c := FindCondition(hdr.awaitingCond) (* interrupts should be on during this call *)
- 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 (* no true condition found, check the lock queue *)
- 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 (* true condition found, transfer the lock *)
- 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;
- (* Await a condition (kernel call). *)
- (* There are two different procedures for locking a protected object in case of priority inverison handling enabled or disabled due to the different
- locking strategies, i.e. there are no header locks in case of priority inversion handling. *)
- PROCEDURE Await*(cond: Condition; slink: ADDRESS; obj: ProtectedObject; flags: SET);
- BEGIN
- IF HandlePriorityInv THEN
- AwaitPriorityInv(cond, slink, obj, flags)
- ELSE
- AwaitNoPriorityInv(cond, slink, obj, flags)
- END
- END Await;
- (* Await a condition if priority inversion handling is disabled. Header locks, preemption and Machine.Objects locks are used. *)
- PROCEDURE AwaitNoPriorityInv(cond: Condition; slink: 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 (* compiler did not generate IF *)
- IF Stats THEN Machine.AtomicInc(NawaitNoIF) END;
- IF cond(slink) THEN
- IF Stats THEN Machine.AtomicInc(NawaitTrue) END;
- RETURN (* condition already true *)
- END
- END;
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- IF StrongChecks THEN
- ASSERT(hdr IS Heaps.ProtRecBlock) (* protected object *)
- END;
- id := Machine.AcquirePreemption ();
- Machine.AcquireObject(hdr.locked); (* must acquire object lock before other locks *)
- r := running[id];
- IF hdr.lockedBy = r THEN (* current process holds exclusive lock *)
- IF StrongChecks THEN ASSERT(hdr.count = -1) END; (* exclusive locked *)
- IF hdr.awaitingCond.head # NIL THEN (* evaluate the waiting conditions *)
- (* we are holding the lock, so the queue can not change (to do: except in TerminateThis) *)
- c := FindCondition(hdr.awaitingCond) (* interrupts should be on during this call *)
- ELSE
- c := NIL
- END;
- IF c = NIL THEN
- Get(hdr.awaitingLock, t);
- IF t = NIL THEN (* none waiting - remove lock *)
- hdr.count := 0; hdr.lockedBy := NIL;
- ELSE (* transfer lock to first waiting process *)
- IF StrongChecks THEN ASSERT(t.mode = AwaitingLock) END;
- TransferLock(hdr, t)
- END;
- ELSE
- TransferLock(hdr, c);
- t := NIL
- END;
- ELSE (* no lock, or some other process may hold the lock, but that's the user's indaba (may be monotonic condition) *)
- Machine.ReleaseObject(hdr.locked);
- Machine.ReleasePreemption;
- HALT(2204) (* await must be exclusive region *)
- END;
- Machine.Acquire(Machine.Objects); (* Put and SwitchTo must be protected *)
- 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;
- (* reschedule *)
- SwitchToNew;
- IF StrongChecks THEN
- ASSERT(cond(slink));
- ASSERT(hdr.lockedBy = r) (* lock held again *)
- END
- END AwaitNoPriorityInv;
- (* Await a condition in case priority inversion handling is enabled. Machine.Objects lock is used. *)
- PROCEDURE AwaitPriorityInv(cond: Condition; slink: ADDRESS; obj: ProtectedObject; flags: SET);
- VAR hdr {UNTRACED}: Heaps.ProtRecBlock; r, c, t: Process; id, maxWaitingPrio, prevMaxWaitingPrio: WORD;
- BEGIN
- IF Stats THEN Machine.AtomicInc(Nawait) END;
- IF 1 IN flags THEN (* compiler did not generate IF *)
- IF Stats THEN Machine.AtomicInc(NawaitNoIF) END;
- IF cond(slink) THEN
- IF Stats THEN Machine.AtomicInc(NawaitTrue) END;
- RETURN (* condition already true *)
- END
- END;
- SYSTEM.GET(SYSTEM.VAL(ADDRESS, obj) + Heaps.HeapBlockOffset, hdr);
- IF StrongChecks THEN
- ASSERT(hdr IS Heaps.ProtRecBlock) (* protected object *)
- END;
- Machine.Acquire(Machine.Objects);
- id := Machine.ID();
- r := running[id];
- IF hdr.lockedBy = r THEN (* current process holds exclusive lock *)
- IF StrongChecks THEN ASSERT(hdr.count = -1) END; (* exclusive locked *)
- maxWaitingPrio := MaxPrio(hdr.waitingPriorities);
- DEC(r.prioRequests[maxWaitingPrio]);
- r.priority := MaxPrio(r.prioRequests);
- IF hdr.awaitingCond.head # NIL THEN (* evaluate the waiting conditions *)
- (* we are holding the lock, so the queue can not change (to do: except in TerminateThis) *)
- c := FindCondition(hdr.awaitingCond) (* interrupts should be on during this call *)
- ELSE
- c := NIL
- END;
- IF c = NIL THEN
- GetMaxPrio(hdr.awaitingLock, t);
- IF t = NIL THEN (* none waiting - remove lock *)
- hdr.count := 0; hdr.lockedBy := NIL;
- ELSE (* transfer lock to first waiting process *)
- IF StrongChecks THEN ASSERT(t.mode = AwaitingLock) END;
- TransferLock(hdr, t);
- END;
- ELSE (* true condition found, transfer the lock *)
- TransferLock(hdr, c);
- t := NIL;
- END;
- ELSE (* no lock, or some other process may hold the lock, but that's the user's indaba (may be monotonic condition) *)
- Machine.Release(Machine.Objects);
- HALT(2204) (* await must be exclusive region *)
- 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 (* it may happen that hdr is not locked - in that case no priority propagation takes place *)
- INC(hdr.waitingPriorities[r.priority])
- END;
- Put(hdr.awaitingCond, r);
- (* reschedule *)
- SwitchToNew;
- IF StrongChecks THEN
- ASSERT(cond(slink));
- ASSERT(hdr.lockedBy = r) (* lock held again *)
- END
- END AwaitPriorityInv;
- (** Update the state snapshot of the current process for GC. (for Processors) *)
- PROCEDURE UpdateState;
- VAR t: Process;
- BEGIN (* interrupts off *)
- Machine.Acquire(Machine.Objects);
- t := running[Machine.ID ()];
- IF t # NIL THEN
- t.state.PC := Machine.CurrentPC(); (* ug: required information for GC with meta data for stack inspection *)
- t.state.SP := SYSTEM.GetStackPointer(); (* ug: not necessarily needed for GC *)
- t.state.BP := SYSTEM.GetFramePointer(); (* ug: necessary information for GC with meta data for stack inspection *)
- END;
- Machine.Release(Machine.Objects)
- END UpdateState;
- (** Start executing user processes. Every processor calls this during initialization. *)
- PROCEDURE Start*;
- VAR id: LONGINT; idle: Idle; new: Process;
- BEGIN (* running at kernel level (not preemptable) *)
- id := Machine.ID (); (* preemption not enabled yet, because we are running at kernel level *)
- NEW(idle); (* create process with MinPriority *)
- Machine.Acquire(Machine.Objects);
- Get(ready.q[MinPriority], new); (* can not use Select here, as it might return a preempted process *)
- ASSERT(~(Preempted IN new.flags)); (* will at least get the Idle process just created *)
- Machine.Release(Machine.Objects);
- running[id] := new; (* schedule new process *)
- 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;
- (* Initialize module. *)
- PROCEDURE Init; (* can not use NEW *)
- VAR
- i: LONGINT;
- BEGIN
- ProcessorHLT := NIL;
- maxReady := High; (* scan all queues at start *)
- lowestAllowedPriority := Low; (* normal case, will be set to GCPriority if GC is running *)
- gcBarrier := {};
- FOR i := 0 TO Machine.MaxCPU - 1 DO running[i] := NIL END;
- FOR i := 0 TO NumPriorities - 1 DO rootedProcesses[i] := NIL END;
- FOR i := 0 TO NumIRQ-1 DO processingIRQ[i] := FALSE END;
- nextProcessID := 0; Machine.ticks := 0;
- traceProcess := NIL;
- terminate := Terminate;
- trap[0] := Halt;
- trap[1] := HaltUnbreakable;
- trapReturn[0] := HaltReturn;
- trapReturn[1] := HaltUnbreakableReturn;
- END Init;
- PROCEDURE InitEventHandling;
- VAR i: LONGINT; clock: Clock; (* realtimeClock: RealtimeClock; *)
- BEGIN
- FOR i := 0 TO NumIRQ-1 DO
- interrupt[i].root := NIL; interrupt[i].process := NIL
- END;
- (* create normal event list *)
- NEW(event); event.next := event; event.prev := event;
- event.trigger := Machine.ticks + MAX(LONGINT) DIV 2;
- (* create normal timer processes *)
- 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;
- PROCEDURE InitPrioRequest;
- VAR
- i: SIZE;
- BEGIN
- FOR i := 0 TO LEN(init.prioRequests) - 1 DO init.prioRequests[i] := 0 END;
- END InitPrioRequest;
- VAR
- (* for compatibility and later extension *)
- TraceProcessHook*: PROCEDURE (prcoess: Process; pc, bp: ADDRESS; stacklow, stackhigh: ADDRESS);
-
- BEGIN
- TraceProcessHook := NIL;
- IF Stats THEN InitStats; END;
- Init;
- (* initialize memory management *)
- Machine.UpdateState; (* for gc *)
- Heaps.CollectGarbage(Modules.root); (* still in single-processor mode *)
- (* now NEW can be used *)
- NEW(ready); (* create the ready queues *)
- Machine.InitInterrupts;
- Machine.Start; (* initialize interrupts *)
- InitEventHandling;
- InitGCHandling;
- Heaps.gcStatus := GCStatusFactory();
- (* create a process for rest of init code, which runs at user level *)
- entry := SYSTEM.GetFramePointer ();
- SYSTEM.GET (entry+AddressSize, entry); (* return address into linker-generated call table *)
- NEW(initObject);
- NewProcess(SYSTEM.VAL (Body, entry), {Resistant}, initObject, init); (* create init process *)
- init.priority := High;
- init.staticPriority := init.priority;
- (* initialize prioRequests for init process *)
- InitPrioRequest;
- INC(init.prioRequests[init.priority]);
- Machine.Acquire(Machine.Objects);
- init.id := -1; Enter(init); init := NIL;
- Machine.Release(Machine.Objects);
- Start (* start it *)
- (* linker call table will end with a call to Terminate. So after executing all module bodies,
- the init process will terminate and other processes created during init will continue running. *)
- 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
- *)
|