123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- (* Runtime support for activities *)
- (* Copyright (C) Florian Negele *)
- (** The module provides the runtime support for activities associated with active objects. *)
- (** It implements a basic task scheduler that distributes the work of concurrent activities to logical processors. *)
- (** In addition, it also provides a framework for implementing synchronisation primitives. *)
- MODULE Activities;
- IMPORT SYSTEM, BaseTypes, Counters, CPU, Processors, Queues, Timer;
- (** Represents one of four different priorities of an activity. *)
- TYPE Priority* = SIZE;
- CONST SafeStackSize = 512 * SIZE OF ADDRESS;
- CONST InitialStackSize = CPU.StackSize;
- CONST MaximumStackSize* = 1024 * InitialStackSize;
- CONST (** Indicates the lowest priority used for idle processors. *) IdlePriority* = 0;
- CONST (** Indicates the default priority of new activities. *) DefaultPriority* = 1;
- CONST (** Indicates a higher priority than the default. *) HighPriority* = 2;
- CONST (** Indicates the highest of all priorities. *) RealtimePriority* = 3;
- CONST LowestPriority = IdlePriority; HighestPriority = RealtimePriority;
- CONST Priorities = HighestPriority - LowestPriority + 1;
- (** Represents a procedure that is called after the execution of an activity has been suspended by the {{{[[Activities.SwitchTo]]}}} procedure. *)
- TYPE SwitchFinalizer* = PROCEDURE (previous {UNTRACED}: Activity; value: ADDRESS);
- (* Represents the stack of an activity. *)
- TYPE Stack = POINTER {DISPOSABLE} TO ARRAY OF CHAR;
- TYPE StackRecord = POINTER {UNSAFE} TO RECORD
- prev {UNTRACED}, next {UNTRACED}: Stack;
- END;
- (** Represents the handler identifying activities that are currently either running or suspended. *)
- TYPE Activity* = OBJECT {DISPOSABLE} (Queues.Item)
- VAR processor {UNTRACED}: POINTER {UNSAFE} TO Processor;
- VAR firstStack {UNTRACED}: Stack;
- VAR stackLimit: ADDRESS;
- VAR quantum := CPU.Quantum: LONGWORD;
- VAR priority: Priority;
- VAR finalizer := NIL: SwitchFinalizer;
- VAR previous: Activity; argument: ADDRESS;
- VAR framePointer: ADDRESS;
- VAR procedure: PROCEDURE;
- VAR object-: BaseTypes.Object;
- VAR bound := FALSE: BOOLEAN;
- VAR startTime-: Timer.Counter;
- VAR time- := 0: HUGEINT;
- VAR stack {UNTRACED}: Stack;
- VAR context*: OBJECT;
- PROCEDURE &InitializeActivity (procedure: PROCEDURE; priority: Priority);
- VAR stackRecord {UNTRACED}: StackRecord; stackFrame {UNTRACED}: BaseTypes.StackFrame;
- VAR StackFrameDescriptor {UNTRACED} EXTERN "BaseTypes.StackFrame": BaseTypes.Descriptor;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- ASSERT (priority < Priorities);
- ASSERT (InitialStackSize > SafeStackSize);
- NEW (stack, InitialStackSize);
- ASSERT (stack # NIL);
- firstStack := stack;
- stackRecord := ADDRESS OF stack[0];
- stackRecord.next := NIL;
- stackRecord.prev := NIL;
- stackLimit := ADDRESS OF stack[SafeStackSize+3* SIZE OF ADDRESS]; SELF.priority := priority;
- framePointer := ADDRESS OF stack[InitialStackSize - 4 * SIZE OF ADDRESS] - CPU.StackDisplacement;
- stackFrame := framePointer + CPU.StackDisplacement;
- stackFrame.caller := Start;
- stackFrame.previous := NIL;
- stackFrame.descriptor := ADDRESS OF StackFrameDescriptor;
- SELF.procedure := procedure;
- END InitializeActivity;
- PROCEDURE ~Finalize;
- VAR address: ADDRESS; stackFrame {UNTRACED}: BaseTypes.StackFrame; currentActivity {UNTRACED}: Activity; stack{UNTRACED}, next{UNTRACED}: Stack; stackRecord{UNTRACED}: StackRecord;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- address := framePointer;
- currentActivity := SYSTEM.GetActivity ()(Activity); SYSTEM.SetActivity (SELF);
- WHILE address # NIL DO
- stackFrame := address + CPU.StackDisplacement;
- IF ODD (stackFrame.descriptor) THEN
- DEC (stackFrame.descriptor);
- stackFrame.Reset;
- address := stackFrame.previous;
- ELSE
- address := stackFrame.descriptor;
- END;
- END;
- SYSTEM.SetActivity (currentActivity);
- stack := firstStack;
- REPEAT
- stackRecord := ADDRESS OF stack[0];
- next := stackRecord.next;
- DISPOSE (stack);
- stack := next;
- UNTIL stack = NIL;
- Finalize^;
- END Finalize;
- END Activity;
- (* Represents a handler for an activity that is associated with an active object. *)
- TYPE Process = OBJECT {DISPOSABLE} (Activity)
- PROCEDURE &InitializeProcess (procedure: PROCEDURE; priority: Priority; object: BaseTypes.Object);
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- InitializeActivity (procedure, priority);
- ASSERT (object # NIL);
- ASSERT (object.action # NIL);
- SELF.object := object;
- object.action.activity := SELF;
- END InitializeProcess;
- PROCEDURE Unlink;
- BEGIN {UNCOOPERATIVE, UNCHECKED} object := NIL;
- END Unlink;
- PROCEDURE ~Finalize;
- VAR currentActivity {UNTRACED}: Activity; item: Queues.Item;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- IF object # NIL THEN
- currentActivity := SYSTEM.GetActivity ()(Activity); SYSTEM.SetActivity (SELF);
- object.action.activity := NIL;
- IF Queues.Dequeue (item, object.action.waitingQueue) THEN Resume (item(Activity)) END;
- Unlink;
- SYSTEM.SetActivity (currentActivity);
- END;
- Finalize^;
- END Finalize;
- END Process;
- (* Stores information per processor. *)
- TYPE Processor = RECORD {ALIGNED (CPU.CacheLineSize)}
- assigning := FALSE: BOOLEAN;
- originalFramePointer: ADDRESS;
- readyQueue: ARRAY Priorities OF Queues.AlignedQueue;
- runningActivity {UNTRACED}: Activity;
- index: SIZE;
- END;
- VAR processors: ARRAY Processors.Maximum OF Processor;
- VAR readyQueue: ARRAY Priorities OF Queues.AlignedQueue;
- VAR working, physicalProcessors, virtualProcessors: Counters.AlignedCounter;
- (** Stores an atomic counter indicating the number of activities that are awaiting interrupts to occur. *)
- (** The scheduler stops its execution if all processors are idle, unless there are activities waiting for interrupts. *)
- VAR awaiting*: Counters.AlignedCounter;
- PROCEDURE StoreActivity EXTERN "Environment.StoreActivity";
- PROCEDURE GetProcessTime-(): HUGEINT;
- VAR activity: Activity; diff: Timer.Counter;
- BEGIN{UNCOOPERATIVE, UNCHECKED}
- activity := SYSTEM.GetActivity ()(Activity);
- diff := Timer.GetCounter()-activity.startTime;
- RETURN activity.time + diff;
- END GetProcessTime;
- (** Returns the handler of the current activity executing this procedure call. *)
- PROCEDURE GetCurrentActivity- (): {UNTRACED} Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED} RETURN SYSTEM.GetActivity ()(Activity);
- END GetCurrentActivity;
- (** Returns the unique index of the processor executing this procedure call. *)
- PROCEDURE GetCurrentProcessorIndex- (): SIZE;
- BEGIN {UNCOOPERATIVE, UNCHECKED} IF SYSTEM.GetActivity () # NIL THEN RETURN SYSTEM.GetActivity ()(Activity).processor.index ELSE RETURN 0 END;
- END GetCurrentProcessorIndex;
- (** Sets the priority of the current activity calling this procedure and returns the previous value. *)
- PROCEDURE SetCurrentPriority- (priority: Priority): Priority;
- VAR currentActivity {UNTRACED}: Activity; previousPriority: Priority;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- ASSERT (priority < Priorities);
- currentActivity := SYSTEM.GetActivity ()(Activity);
- previousPriority := currentActivity.priority;
- currentActivity.priority := priority;
- RETURN previousPriority;
- END SetCurrentPriority;
- (** Binds the calling activity to the currently executing processor. *)
- PROCEDURE BindToCurrentProcessor-;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- SYSTEM.GetActivity ()(Activity).bound := TRUE;
- END BindToCurrentProcessor;
- (** Returns whether there is an activity that is ready to run and has at least the specified priority. *)
- PROCEDURE Select- (VAR activity: Activity; minimum: Priority): BOOLEAN;
- VAR processor {UNTRACED}: POINTER {UNSAFE} TO Processor;
- VAR priority := HighestPriority + 1: Priority; item: Queues.Item;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- processor := SYSTEM.GetActivity ()(Activity).processor;
- REPEAT
- DEC (priority);
- IF Queues.Dequeue (item, processor.readyQueue[priority]) THEN
- activity := item(Activity); RETURN TRUE;
- ELSIF Queues.Dequeue (item, readyQueue[priority]) THEN
- activity := item(Activity);
- IF activity.bound & (activity.processor # processor) THEN
- Enqueue (activity, ADDRESS OF activity.processor.readyQueue[activity.priority]);
- ELSE
- RETURN TRUE;
- END;
- END;
- UNTIL priority = minimum;
- RETURN FALSE;
- END Select;
- (** Performs a cooperative task switch by suspending the execution of the current activity and resuming the execution of any other activity that is ready to continue. *)
- (** This procedure is called by the compiler whenever it detects that the time quantum of the current activity has expired. *)
- PROCEDURE Switch-;
- VAR currentActivity {UNTRACED}, nextActivity: Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- currentActivity := SYSTEM.GetActivity ()(Activity);
- IF Select (nextActivity, currentActivity.priority) THEN
- SwitchTo (nextActivity, Enqueue, ADDRESS OF readyQueue[currentActivity.priority]);
- FinalizeSwitch;
- ELSE
- currentActivity.quantum := CPU.Quantum;
- END;
- END Switch;
- (* Switch finalizer that enqueues the previous activity to the specified ready queue. *)
- PROCEDURE Enqueue (previous {UNTRACED}: Activity; queue {UNTRACED}: POINTER {UNSAFE} TO Queues.Queue);
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- Queues.Enqueue (previous, queue^);
- IF ADDRESS OF queue^ = ADDRESS OF readyQueue[IdlePriority] THEN RETURN END;
- IF Counters.Read (working) < Processors.count THEN Processors.ResumeAllProcessors END;
- END Enqueue;
- (** Resumes the execution of an activity that was suspended by a call to the {{{[[Activities.SwitchTo]]}}} procedure beforehand. *)
- PROCEDURE Resume- (activity: Activity);
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- ASSERT (activity # NIL);
- Enqueue (activity, ADDRESS OF readyQueue[activity.priority])
- END Resume;
- (** Performs a synchronous task switch. *)
- (** The resumed activity continues its execution by first calling the specified finalizer procedure with the given argument. *)
- (** Each invocation of this procedure must be directly followed by a call to the {{{[[Activities.FinalizeSwitch]]}}} procedure. *)
- PROCEDURE SwitchTo- (VAR activity: Activity; finalizer: SwitchFinalizer; argument: ADDRESS);
- VAR currentActivity {UNTRACED}, nextActivity {UNTRACED}: Activity; diff: Timer.Counter;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- IF activity.bound & (activity.processor # SYSTEM.GetActivity ()(Activity).processor) THEN
- REPEAT UNTIL Select (nextActivity, IdlePriority);
- Resume (activity); activity := nextActivity;
- END;
- currentActivity := SYSTEM.GetActivity ()(Activity);
- currentActivity.framePointer := SYSTEM.GetFramePointer ();
- currentActivity.quantum := CPU.Quantum;
- diff := Timer.GetCounter() - currentActivity.startTime;
- currentActivity.time := currentActivity.time + diff;
- nextActivity := activity;
- nextActivity.processor := currentActivity.processor;
- nextActivity.finalizer := finalizer;
- nextActivity.argument := argument;
- nextActivity.previous := currentActivity;
- nextActivity.processor.runningActivity := nextActivity;
- nextActivity.startTime := Timer.GetCounter();
- activity := NIL;
- SYSTEM.SetActivity (nextActivity); StoreActivity;
- SYSTEM.SetFramePointer (nextActivity.framePointer);
- END SwitchTo;
- (** Finalizes a task switch performed by calling the switch finalizer of the previously suspended activity. *)
- (** This procedure must be called after each invocation of the {{{[[Activities.SwitchTo]]}}} procedure. *)
- PROCEDURE FinalizeSwitch-;
- VAR currentActivity {UNTRACED}: Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- currentActivity := SYSTEM.GetActivity ()(Activity);
- IF currentActivity.finalizer # NIL THEN currentActivity.finalizer (currentActivity.previous, currentActivity.argument) END;
- currentActivity.finalizer := NIL; currentActivity.previous := NIL;
- END FinalizeSwitch;
- (* Entry point for new activities. *)
- PROCEDURE Start;
- VAR currentActivity {UNTRACED}: Activity;
- VAR procedure {UNTRACED}: POINTER {UNSAFE} TO RECORD body: PROCEDURE (object {UNTRACED}: OBJECT) END;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- FinalizeSwitch;
- currentActivity := SYSTEM.GetActivity ()(Activity);
- procedure := ADDRESS OF currentActivity.procedure;
- procedure.body (currentActivity.object);
- TerminateCurrentActivity;
- END Start;
- (** This procedure is called by the compiler for each {{{NEW}}} statement that creates an active object. *)
- (** It associates an active object with a new activity that begins its execution with the specified body procedure. *)
- PROCEDURE Create- (body: PROCEDURE; priority: Priority; object {UNTRACED}: BaseTypes.Object);
- VAR activity: Process;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- IF priority = IdlePriority THEN priority := SYSTEM.GetActivity ()(Activity).priority END;
- NEW (activity, body, priority, object);
- ASSERT (activity # NIL);
- activity.context := GetCurrentActivity ().context;
- Resume (activity);
- END Create;
- (** Creates an activity that pretends to be executed on a distinct processor. *)
- PROCEDURE CreateVirtualProcessor- (): {UNTRACED} Activity;
- VAR activity {UNTRACED}: Activity; index: SIZE;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- NEW (activity, NIL, DefaultPriority);
- ASSERT (activity # NIL);
- index := Counters.Increment (virtualProcessors, 1) + Processors.count;
- ASSERT (index < Processors.Maximum);
- activity.processor := ADDRESS OF processors[index];
- activity.processor.index := index;
- activity.bound := TRUE;
- RETURN activity;
- END CreateVirtualProcessor;
- (** Temporarily exchanges the currently running activity with a virtual processor in order to call the specified procedure in a different context. *)
- PROCEDURE CallVirtual- (procedure: PROCEDURE (value: ADDRESS); value: ADDRESS; processor {UNTRACED}: Activity);
- VAR currentActivity {UNTRACED}: Activity; stackPointer: ADDRESS;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- ASSERT (processor # NIL);
- currentActivity := SYSTEM.GetActivity ()(Activity); stackPointer := SYSTEM.GetStackPointer (); SYSTEM.SetActivity (processor); StoreActivity;
- SYSTEM.SetStackPointer (ADDRESS OF processor.stack[LEN (processor.stack) - CPU.StackDisplacement]);
- procedure (value); SYSTEM.SetActivity (currentActivity); StoreActivity; SYSTEM.SetStackPointer (stackPointer);
- END CallVirtual;
- (** Creates a new activity that calls the specified procedure. *)
- PROCEDURE Call- (procedure: PROCEDURE);
- VAR activity: Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- NEW (activity, procedure, DefaultPriority);
- ASSERT (activity # NIL);
- Resume (activity);
- END Call;
- (** Starts the scheduler on the current processor by creating a new activity that calls the specified procedure. *)
- (** This procedure is called by the runtime system once during the initialization of each processor. *)
- PROCEDURE Execute- (procedure: PROCEDURE);
- VAR previousActivity {UNTRACED}: Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- SYSTEM.SetActivity (NIL);
- BeginExecution (procedure);
- previousActivity := SYSTEM.GetActivity ()(Activity);
- previousActivity.processor.runningActivity := NIL;
- SYSTEM.SetActivity (NIL);
- Dispose (previousActivity, NIL);
- END Execute;
- (* Turns the calling procedure temporarily into an activity that begins its execution with the specified procedure. *)
- PROCEDURE BeginExecution (procedure: PROCEDURE);
- VAR activity {UNTRACED}: Activity; index: SIZE;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- NEW (activity, procedure, DefaultPriority);
- ASSERT (activity # NIL);
- index := Counters.Increment (physicalProcessors, 1);
- ASSERT (index < Processors.count);
- activity.processor := ADDRESS OF processors[index];
- activity.processor.originalFramePointer := SYSTEM.GetFramePointer ();
- activity.processor.runningActivity := activity; activity.processor.index := index;
- ASSERT (Counters.Increment (working, 1) < Processors.count);
- IF (index = 0) & (Processors.count > 1) THEN Processors.StartAll END;
- SYSTEM.SetActivity (activity); SYSTEM.SetFramePointer (activity.framePointer);
- END BeginExecution;
- (* Yields the execution of the current activity to any activity with the given minimal priority. *)
- PROCEDURE YieldExecution (minimum: Priority; finalizer: SwitchFinalizer; value: ADDRESS);
- VAR nextActivity: Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- LOOP
- IF Select (nextActivity, minimum) THEN
- SwitchTo (nextActivity, finalizer, value);
- FinalizeSwitch;
- ELSE
- IF Counters.Decrement (working, 1) + Counters.Read (awaiting) > 1 THEN Processors.SuspendCurrentProcessor END;
- IF Counters.Increment (working, 1) + Counters.Read (awaiting) = 0 THEN EXIT END;
- END;
- END;
- END YieldExecution;
- (* This procedure returns to the procedure that called BeginExecution. *)
- PROCEDURE EndExecution;
- VAR currentActivity {UNTRACED}: Activity;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- currentActivity := SYSTEM.GetActivity ()(Activity);
- currentActivity.framePointer := SYSTEM.GetFramePointer ();
- IF Counters.Decrement (working, 1) < Processors.count THEN Processors.ResumeAllProcessors END;
- SYSTEM.SetFramePointer (currentActivity.processor.originalFramePointer);
- END EndExecution;
- (** This is the default procedure for initially idle processors starting the scheduler using the {{{[[Activities.Execute]]}}} procedure. *)
- PROCEDURE Idle-;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- ASSERT (SetCurrentPriority (IdlePriority) = DefaultPriority);
- YieldExecution (IdlePriority + 1, Enqueue, ADDRESS OF readyQueue[IdlePriority]); EndExecution;
- END Idle;
- (** Terminates the execution of the current activity calling this procedure. *)
- (** This procedure is also invoked at the end of the body of an active object. *)
- PROCEDURE {NORETURN} TerminateCurrentActivity-;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- YieldExecution (IdlePriority, Dispose, NIL);
- EndExecution; HALT (1234);
- END TerminateCurrentActivity;
- (* Switch finalizer that disposes the resources of the terminated activity. *)
- PROCEDURE Dispose (previous {UNTRACED}: Activity; value: ADDRESS);
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- DISPOSE (previous);
- END Dispose;
- (** This procedure is called by the compiler while executing a {{{WAIT}}} statement. *)
- (** It awaits the termination of all activities associated with an active object. *)
- PROCEDURE Wait- (object {UNTRACED}: BaseTypes.Object);
- VAR nextActivity: Activity; item: Queues.Item;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- ASSERT (object # NIL);
- IF object.action = NIL THEN RETURN END;
- IF object.action.activity = NIL THEN RETURN END;
- REPEAT UNTIL Select (nextActivity, IdlePriority);
- SwitchTo (nextActivity, EnqueueWaiting, object.action); FinalizeSwitch;
- WHILE Queues.Dequeue (item, object.action.waitingQueue) DO Resume (item (Activity)) END;
- END Wait;
- (* Switch finalizer that enqueues acitivities waiting on an active object. *)
- PROCEDURE EnqueueWaiting (previous {UNTRACED}: Activity; action {UNTRACED}: POINTER {UNSAFE} TO BaseTypes.Action);
- VAR item: Queues.Item;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- Queues.Enqueue (previous, action.waitingQueue);
- IF action.activity # NIL THEN RETURN END;
- IF Queues.Dequeue (item, action.waitingQueue) THEN Resume (item (Activity)) END;
- END EnqueueWaiting;
- PROCEDURE ReturnToStackSegment*;
- VAR
- stackFrame {UNTRACED}: BaseTypes.StackFrame;
- currentActivity {UNTRACED}: Activity;
- newStack {UNTRACED}: Stack;
- stackRecord {UNTRACED}: StackRecord;
- BEGIN{UNCOOPERATIVE, UNCHECKED}
- (* old stack pointer and base pointer have been pushed again, we have to revert this *)
- stackFrame := SYSTEM.GetFramePointer();
- (*
- TRACE(stackFrame.caller);
- TRACE(stackFrame.previous);
- previousFrame := stackFrame.previous;
- TRACE(ADDRESS OF previousFrame.caller + SIZE OF ADDRESS);
- TRACE(previousFrame.caller);
- TRACE(previousFrame.previous);
- *)
- currentActivity := SYSTEM.GetActivity ()(Activity);
- stackRecord := ADDRESS OF currentActivity.stack[0];
- newStack := stackRecord.prev;
- currentActivity.stack := newStack;
- currentActivity.stackLimit := ADDRESS OF newStack[SafeStackSize + 3 * SIZE OF ADDRESS];
- END ReturnToStackSegment;
- PROCEDURE {NOPAF} ReturnToStackSegment0;
- BEGIN{UNCOOPERATIVE, UNCHECKED}
- CPU.SaveResult;
- ReturnToStackSegment;
- CPU.RestoreResultAndReturn;
- END ReturnToStackSegment0;
- (** Expands the stack memory of the current activity to include the specified stack address and returns the new stack pointer to be set after the call. *)
- PROCEDURE ExpandStack- (address: ADDRESS; parSize: SIZE): ADDRESS;
- VAR
- currentActivity {UNTRACED}: Activity;
- varSize, minSize, newSize: SIZE; sp: ADDRESS;
- newStack {UNTRACED}: POINTER {DISPOSABLE} TO ARRAY OF CHAR;
- stackFrame {UNTRACED}, previousFrame {UNTRACED}, newFrame {UNTRACED}: BaseTypes.StackFrame;
- stackRecord{UNTRACED}, newStackRecord{UNTRACED}: StackRecord;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- (* check for valid argument *)
- currentActivity := SYSTEM.GetActivity ()(Activity);
- stackFrame := SYSTEM.GetFramePointer ();
- previousFrame := stackFrame.previous;
- varSize := stackFrame.previous - address;
- (*
- TRACE(SYSTEM.GetFramePointer(), address, varSize, parSize, size, stackFrame.caller);
- *)
- ASSERT(varSize >= 0);
- ASSERT(parSize >= 0);
- newSize := LEN (currentActivity.stack); (* current stack size *)
- minSize := SafeStackSize + parSize + varSize + 3 * SIZEOF(ADDRESS) (* stack frame *) + 3 * SIZEOF(ADDRESS) (* prev, next *);
- REPEAT INC (newSize, newSize) UNTIL newSize >= minSize;
- ASSERT (newSize <= MaximumStackSize);
- stackRecord := ADDRESS OF currentActivity.stack[0];
- newStack := stackRecord.next;
- IF (newStack = NIL) OR (LEN(newStack) < newSize) THEN
- NEW (newStack, newSize);
- ASSERT (newStack # NIL);
- newStackRecord := ADDRESS OF newStack[0];
- newStackRecord.prev := currentActivity.stack;
- newStackRecord.next := NIL;
- stackRecord.next := newStack;
- ELSE
- newStackRecord := ADDRESS OF newStack[0];
- ASSERT(newStackRecord.prev = currentActivity.stack);
- ASSERT(stackRecord.next = newStack);
- END;
- newSize := LEN(newStack);
- newFrame := ADDRESS OF newStack[0] + newSize- parSize - 3*SIZE OF ADDRESS;
- newFrame.previous := stackFrame.previous;
- newFrame.descriptor := previousFrame.descriptor;
- newFrame.caller := ReturnToStackSegment0;
- previousFrame.descriptor := stackFrame.descriptor; (* trick to get a base stack frame descriptor *)
- stackFrame.previous := newFrame;
- SYSTEM.MOVE(ADDRESS OF previousFrame.caller + SIZE OF ADDRESS, ADDRESS OF newFrame.caller + SIZE OF ADDRESS, parSize); (* copy parameters *)
- sp := ADDRESSOF(newFrame.descriptor) - varSize;
- DISPOSE (currentActivity.stack); currentActivity.stack := newStack;
- currentActivity.stackLimit := ADDRESS OF newStack[SafeStackSize + 3 * SIZE OF ADDRESS];
- RETURN sp;
- END ExpandStack;
- (** Returns whether the specified address corresponds to a local variable that resides on the stack of the current activity calling this procedure. *)
- PROCEDURE IsLocalVariable- (address: ADDRESS): BOOLEAN;
- VAR currentActivity {UNTRACED}: Activity; begin, end: ADDRESS; stack {UNTRACED}: Stack; stackRecord {UNTRACED}: StackRecord;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- currentActivity := SYSTEM.GetActivity ()(Activity);
- IF currentActivity = NIL THEN RETURN FALSE END;
- stack := currentActivity.firstStack;
- REPEAT
- begin := ADDRESS OF stack[0];
- end := begin + LEN (stack);
- IF (address >= begin) & (address < end) THEN RETURN TRUE END;
- stackRecord := begin;
- stack := stackRecord.next;
- UNTIL stack = NIL;
- RETURN FALSE;
- END IsLocalVariable;
- (** Returns whether any activity is currently executing an assignment statement. *)
- PROCEDURE AssignmentsInProgress- (): BOOLEAN;
- VAR i: SIZE;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- FOR i := 0 TO Processors.Maximum - 1 DO IF processors[i].assigning THEN RETURN TRUE END END; RETURN FALSE;
- END AssignmentsInProgress;
- (** Terminates the module and disposes all of its resources. *)
- PROCEDURE Terminate-;
- VAR priority: Priority;
- BEGIN {UNCOOPERATIVE, UNCHECKED}
- FOR priority := LowestPriority TO HighestPriority DO
- Queues.Dispose (readyQueue[priority]);
- END;
- END Terminate;
- END Activities.
|