blob: 046352df42b1fc0deef6de0e60762599bc27ba71 [file] [log] [blame]
(* Executive.mod provides a simple multitasking executive.
Copyright (C) 2002-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE Executive[MAX(PROTECTION)] ;
FROM SYSTEM IMPORT ADDRESS, PROCESS, LISTEN, ADR,
NEWPROCESS, TRANSFER, IOTRANSFER, ListenLoop,
TurnInterrupts ;
FROM COROUTINES IMPORT PROTECTION ;
FROM SysStorage IMPORT ALLOCATE, DEALLOCATE ;
FROM StrLib IMPORT StrCopy ;
FROM StrLib IMPORT StrLen ;
FROM NumberIO IMPORT CardToStr ;
FROM Debug IMPORT DebugString, Halt ;
(* IMPORT gdb ; *)
CONST
MaxCharsInName = 15 ;
IdleStackSize = 16 * 1024 * 1024 ;
TYPE
SEMAPHORE = POINTER TO Semaphore ; (* defines dijkstra's semaphores *)
Semaphore = RECORD
Value : CARDINAL ; (* semaphore value *)
SemName: EntityName ; (* semaphore name for debugging *)
Who : DESCRIPTOR ; (* queue of waiting processes *)
ExistsQ: SemQueue ; (* list of existing semaphores *)
END ;
DESCRIPTOR= POINTER TO Descriptor ; (* handle onto a process *)
Descriptor= RECORD
Volatiles : PROCESS ; (* process volatile environment *)
ReadyQ : DesQueue ; (* queue of ready processes *)
ExistsQ : DesQueue ; (* queue of existing processes *)
SemaphoreQ : DesQueue ; (* queue of waiting processes *)
Which : SEMAPHORE ; (* which semaphore are we waiting*)
RunName : EntityName ; (* process name for debugging *)
Status : State ; (* state of process *)
RunPriority: Priority ; (* runtime priority of process *)
Size : CARDINAL ; (* Maximum stack size *)
Start : ADDRESS ; (* Stack start *)
Debugged : BOOLEAN ; (* Does user want to debug a *)
(* deadlocked process? *)
END ;
DesQueue = RECORD
Right,
Left : DESCRIPTOR ;
END ;
SemQueue = RECORD
Right,
Left : SEMAPHORE ;
END ;
EntityName= ARRAY [0..MaxCharsInName] OF CHAR ;
Priority = (idle, lo, hi) ; (* process run priority *)
State = (Runnable, Suspended, WaitOnSem, WaitOnInt) ;
VAR
ExistsQueue : DESCRIPTOR ; (* List of existing processes *)
RunQueue : ARRAY Priority OF DESCRIPTOR ;
(* List of runnable processes *)
CurrentProcess: DESCRIPTOR ;
AllSemaphores : SEMAPHORE ; (* List of all semaphores *)
GarbageItem : DESCRIPTOR ; (* Descriptor destined to free *)
(*
Assert -
*)
PROCEDURE Assert (c: BOOLEAN; file: ARRAY OF CHAR; line: CARDINAL;
function: ARRAY OF CHAR) ;
BEGIN
IF NOT c
THEN
Ps ;
Halt ('assert failed', file, function, line)
END
END Assert ;
(*
InitProcess - initializes a process which is held in the suspended
state. When the process is resumed it will start executing
procedure, p. The process has a maximum stack size of,
StackSize, bytes and its textual name is, Name.
The StackSize should be at least 5000 bytes.
*)
PROCEDURE InitProcess (p: PROC;
StackSize: CARDINAL;
Name: ARRAY OF CHAR) : DESCRIPTOR ;
VAR
d : DESCRIPTOR ;
ToOldState: PROTECTION ;
db : ARRAY [0..80] OF CHAR ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
NEW(d) ;
WITH d^ DO
Size := StackSize ;
(* allocate space for this processes stack *)
ALLOCATE(Start, StackSize) ;
NEWPROCESS(p, Start, StackSize, Volatiles) ; (* create volatiles *)
InitQueue(ReadyQ) ; (* not on the ready queue as suspended *)
AddToExists(d) ; (* add process to the exists queue *)
InitQueue(SemaphoreQ) ; (* not on a semaphore queue yet *)
Which := NIL ; (* not on a semaphore queue yet *)
StrCopy(Name, RunName) ; (* copy name into descriptor for debugging *)
Status := Suspended ; (* this process will be suspended *)
RunPriority := lo ; (* all processes start off at lo priority *)
Debugged := FALSE ; (* no need to debug deadlock yet! *)
END ;
(* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
RETURN( d ) (* and return a descriptor to the caller *)
END InitProcess ;
(*
KillProcess - kills the current process. Notice that if InitProcess
is called again, it might reuse the DESCRIPTOR of the
killed process. It is the responsibility of the caller
to ensure all other processes understand this process
is different.
*)
PROCEDURE KillProcess ;
VAR
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
SubFromReady(CurrentProcess) ;
SubFromExists(ExistsQueue, CurrentProcess) ;
GarbageItem := CurrentProcess ;
Reschedule ;
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END KillProcess ;
(*
Resume - resumes a suspended process. If all is successful then the process, p,
is returned. If it fails then NIL is returned.
*)
PROCEDURE Resume (d: DESCRIPTOR) : DESCRIPTOR ;
VAR
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
WITH d^ DO
IF Status=Suspended
THEN
(* legal state transition *)
Status := Runnable ; (* change status *)
AddToReady(d) ; (* add to run queue *)
RunQueue[RunPriority] := d ; (* make d at top of q *)
Reschedule (* check whether this process has a higher run priority *)
ELSE
(* we are trying to Resume a process which is *)
Halt ('trying to resume a process which is not suspended',
__FILE__, __FUNCTION__, __LINE__) ;
RETURN( NIL ) (* not held in a Suspended state - error *)
END
END ;
(* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
RETURN( d )
END Resume ;
(*
Suspend - suspend the calling process.
The process can only continue running if another process
Resumes it.
*)
PROCEDURE Suspend ;
VAR
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
WITH CurrentProcess^ DO
Status := Suspended
END ;
SubFromReady(CurrentProcess) ;
Reschedule ;
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END Suspend ;
(*
InitSemaphore - creates a semaphore whose initial value is, v, and
whose name is, Name.
*)
PROCEDURE InitSemaphore (v: CARDINAL; Name: ARRAY OF CHAR) : SEMAPHORE ;
VAR
s : SEMAPHORE ;
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
NEW(s) ;
WITH s^ DO
Value := v ; (* initial value of semaphore *)
StrCopy(Name, SemName) ; (* save the name for future debugging *)
Who := NIL ; (* no one waiting on this semaphore yet *)
AddToSemaphoreExists(s) ; (* add semaphore to exists list *)
END ;
(* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
RETURN( s )
END InitSemaphore ;
(*
Wait - performs dijkstra's P operation on a semaphore.
A process which calls this procedure will
wait until the value of the semaphore is > 0
and then it will decrement this value.
*)
PROCEDURE Wait (s: SEMAPHORE) ;
VAR
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
WITH s^ DO
IF Value>0
THEN
DEC( Value )
ELSE
SubFromReady(CurrentProcess) ; (* remove from run q *)
IF Who=CurrentProcess
THEN
Ps ;
Halt ('we are already on sem',
__FILE__, __FUNCTION__, __LINE__)
END ;
AddToSemaphore(Who, CurrentProcess) ; (* add to semaphore q *)
CurrentProcess^.Status := WaitOnSem ; (* set new status *)
CurrentProcess^.Which := s ; (* debugging aid *)
Reschedule (* find next process *)
END
END ;
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END Wait ;
(*
Signal - performs dijkstra's V operation on a semaphore.
A process which calls the procedure will increment
the semaphores value.
*)
PROCEDURE Signal (s: SEMAPHORE) ;
VAR
ToOldState: PROTECTION ;
d : DESCRIPTOR ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
WITH s^ DO
IF Who=NIL
THEN
INC( Value ) (* no process waiting *)
ELSE
d := SubFromSemaphoreTop(Who) ; (* remove process from semaphore q *)
d^.Which := NIL ; (* no longer waiting on semaphore *)
d^.Status := Runnable ; (* set new status *)
AddToReady(d) ; (* add process to the run queue *)
Reschedule (* find out whether there is a *)
(* higher priority to run. *)
END
END ;
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END Signal ;
(*
WaitForIO - waits for an interrupt to occur on vector, VectorNo.
*)
PROCEDURE WaitForIO (VectorNo: CARDINAL) ;
VAR
Calling : DESCRIPTOR ;
Next : PROCESS ;
ToOldState: PROTECTION ;
r : INTEGER ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; *)
(*
DebugString('inside WaitForIO ') ;
DebugString(CurrentProcess^.RunName) ;
DebugString('\n') ;
*)
Assert(CurrentProcess^.Status=Runnable,
__FILE__, __LINE__, __FUNCTION__) ;
SubFromReady(CurrentProcess) ; (* remove process from run queue *)
(*
alter run priority to hi as all processes waiting for an interrupt
are scheduled to run at the highest priority.
*)
WITH CurrentProcess^ DO
Status := WaitOnInt ; (* it will be blocked waiting for an interrupt. *)
RunPriority := hi ; (* this (hopefully) allows it to run as soon as *)
(* the interrupt occurs. *)
END ;
Calling := CurrentProcess ; (* process which called WaitForIO *)
CurrentProcess := NextReady() ; (* find next process to run while we wait *)
Next := CurrentProcess^.Volatiles ;
(*
This is quite complicated. We transfer control to the next process saving
our volatile environment into the Calling process descriptor volatiles.
When an interrupt occurs the calling process will be resumed and the
interrupted process volatiles will be placed into Next.
*)
IOTRANSFER(Calling^.Volatiles, Next, VectorNo) ;
(*
At this point the interrupt has just occurred and the volatiles of
the interrupted process are in Next. Next is the current process
and so we must save them before picking up the Calling descriptor.
*)
CurrentProcess^.Volatiles := Next ; (* carefully stored away *)
CurrentProcess := Calling ; (* update CurrentProcess *)
(*
DebugString(CurrentProcess^.RunName) ;
*)
CurrentProcess^.Status := Runnable ; (* add to run queue *)
AddToReady(CurrentProcess) ;
(*
DebugString(' finishing WaitForIO\n') ;
*)
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END WaitForIO ;
(*
Ps - displays a process list together with relevant their status.
*)
PROCEDURE Ps ;
VAR
ToOldState: PROTECTION ;
p : DESCRIPTOR ;
s : SEMAPHORE ;
a : ARRAY [0..5] OF CHAR ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
p := ExistsQueue ;
IF p#NIL
THEN
REPEAT
DisplayProcess(p) ;
p := p^.ExistsQ.Right
UNTIL p=ExistsQueue
END ;
s := AllSemaphores ;
IF s#NIL
THEN
REPEAT
WITH s^ DO
DebugString(SemName) ;
WriteNSpaces(MaxCharsInName-StrLen(SemName)) ;
CardToStr(Value, 0, a) ;
DebugString(a) ;
DebugString('\n')
END ;
s := s^.ExistsQ.Right
UNTIL s=AllSemaphores
END ;
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END Ps ;
(*
DisplayProcess - displays the process, p, together with its status.
*)
PROCEDURE DisplayProcess (p: DESCRIPTOR) ;
VAR
a: ARRAY [0..4] OF CHAR ;
BEGIN
WITH p^ DO
DebugString(RunName) ; WriteNSpaces(MaxCharsInName-StrLen(RunName)) ;
CASE RunPriority OF
idle: DebugString(' idle ') |
lo : DebugString(' lo ') |
hi : DebugString(' hi ')
END ;
CASE Status OF
Runnable : DebugString('runnable ') |
Suspended: DebugString('suspended') |
WaitOnSem: DebugString('waitonsem (') ;
DebugString(Which^.SemName) ;
DebugString(')') |
WaitOnInt: DebugString('waitonint')
END ;
DebugString('\n')
END
END DisplayProcess ;
(*
WriteNSpaces - writes, n, spaces.
*)
PROCEDURE WriteNSpaces (n: CARDINAL) ;
BEGIN
WHILE n>0 DO
DebugString(' ') ;
DEC(n)
END
END WriteNSpaces ;
(*
GetCurrentProcess - returns the descriptor of the current running
process.
*)
PROCEDURE GetCurrentProcess () : DESCRIPTOR ;
VAR
ToOldState: PROTECTION ;
p : DESCRIPTOR ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
p := CurrentProcess ;
(* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
RETURN( p )
END GetCurrentProcess ;
(*
RotateRunQueue - rotates the process run queue.
*)
PROCEDURE RotateRunQueue ;
VAR
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
(* we only need to rotate the lo priority processes as:
idle - should only have one process (the idle process)
hi - are the device drivers which most of the time are performing
WaitForIO
*)
IF RunQueue[lo]#NIL
THEN
RunQueue[lo] := RunQueue[lo]^.ReadyQ.Right
END ;
(* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
END RotateRunQueue ;
(*
ProcessName - displays the name of process, d, through
DebugString.
*)
PROCEDURE ProcessName (d: DESCRIPTOR) ;
BEGIN
DebugString(d^.RunName)
END ProcessName ;
(*
DebugProcess -
*)
PROCEDURE DebugProcess (d: DESCRIPTOR) ;
VAR
ToOldState: PROTECTION ;
BEGIN
(* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; *)
WITH d^ DO
IF Status=WaitOnSem
THEN
DebugString('debugging process (') ;
DebugString(RunName) ;
DebugString(') was waiting on semaphore (') ;
DebugString(Which^.SemName) ;
DebugString(')\n') ;
SubFromSemaphore(Which^.Who, d) ;
AddToReady(d) ;
Status := Runnable ;
Debugged := TRUE ;
Reschedule
ELSE
DebugString('can only debug deadlocked processes (') ;
DebugString(RunName) ;
DebugString(') which are waiting on a semaphore\n')
END
END ;
(* ToOldState := TurnInterrupts(ToOldState) *)
END DebugProcess ;
(*
CheckDebugged - checks to see whether the debugged flag has
been set by the debugger.
TRUE is returned if the process was debugged.
FALSE is returned if the process was not debugged.
*)
PROCEDURE CheckDebugged () : BOOLEAN ;
BEGIN
WITH CurrentProcess^ DO
IF Debugged
THEN
(*
You will see this comment after you have enabled a
deadlocked process to continue via the gdb command:
print Executive_DebugProcess(d)
debugger caused deadlocked process to continue
*)
(* gdb.breakpoint ; *)
Debugged := FALSE ;
SubFromReady(CurrentProcess) ;
AddToSemaphore(Which^.Who, CurrentProcess) ;
(* add it back to the queue sem *)
Status := WaitOnSem ;
RETURN( TRUE )
END
END ;
RETURN( FALSE )
END CheckDebugged ;
(*
Reschedule - reschedules to the highest runnable process.
*)
PROCEDURE Reschedule ;
BEGIN
(*
the repeat loop allows us to debug a process even when it is
technically waiting on a semaphore. We run the process into
a breakpoint and then back into this schedule routine.
This is really useful when trying to find out why processes have
deadlocked.
*)
REPEAT
ScheduleProcess
UNTIL NOT CheckDebugged()
END Reschedule ;
(*
ScheduleProcess - finds the highest priority Runnable process and
then transfers control to it.
*)
PROCEDURE ScheduleProcess ;
VAR
From,
Highest: DESCRIPTOR ;
BEGIN
Highest := NextReady() ;
(* rotate ready Q to ensure fairness *)
RunQueue[Highest^.RunPriority] := Highest^.ReadyQ.Right ;
(* no need to transfer if Highest=CurrentProcess *)
IF Highest#CurrentProcess
THEN
From := CurrentProcess ;
(*
DebugString('context switching from ') ; DebugString(From^.RunName) ;
*)
(* alter CurrentProcess before we TRANSFER *)
CurrentProcess := Highest ;
(*
DebugString(' to ') ; DebugString(CurrentProcess^.RunName) ;
*)
TRANSFER(From^.Volatiles, Highest^.Volatiles) ;
(*
; DebugString(' (') ; DebugString(CurrentProcess^.RunName) ;
DebugString(')\n') ;
*)
CheckGarbageCollect
END
END ScheduleProcess ;
(*
NextReady - returns the highest priority Runnable process.
*)
PROCEDURE NextReady () : DESCRIPTOR ;
VAR
Highest: DESCRIPTOR ;
Pri : Priority ;
BEGIN
Highest := NIL ;
FOR Pri := idle TO hi DO
IF RunQueue[Pri]#NIL
THEN
Highest := RunQueue[Pri]
END
END ;
Assert(Highest#NIL, __FILE__, __LINE__, __FUNCTION__) ;
RETURN( Highest )
END NextReady ;
(*
CheckGarbageCollect - checks to see whether GarbageItem is set
and if so it deallocates storage associated
with this descriptor.
*)
PROCEDURE CheckGarbageCollect ;
BEGIN
IF GarbageItem#NIL
THEN
WITH GarbageItem^ DO
DEALLOCATE(Start, Size)
END ;
DISPOSE(GarbageItem) ;
GarbageItem := NIL
END
END CheckGarbageCollect ;
(*
AddToExists - adds item, Item, to the exists queue.
*)
PROCEDURE AddToExists (Item: DESCRIPTOR) ;
BEGIN
IF ExistsQueue=NIL
THEN
ExistsQueue := Item ; (* Head is empty therefore make *)
Item^.ExistsQ.Left := Item ; (* Item the only entry on this *)
Item^.ExistsQ.Right := Item (* queue. *)
ELSE
Item^.ExistsQ.Right := ExistsQueue ; (* Add Item to the end of queue *)
Item^.ExistsQ.Left := ExistsQueue^.ExistsQ.Left ;
ExistsQueue^.ExistsQ.Left^.ExistsQ.Right := Item ;
ExistsQueue^.ExistsQ.Left := Item
END
END AddToExists ;
(*
SubFromExists - removes a process, Item, from the exists queue, Head.
*)
PROCEDURE SubFromExists (VAR Head: DESCRIPTOR; Item: DESCRIPTOR) ;
BEGIN
IF (Item^.ExistsQ.Right=Head) AND (Item=Head)
THEN
Head := NIL
ELSE
IF Head=Item
THEN
Head := Head^.ExistsQ.Right
END ;
Item^.ExistsQ.Left^.ExistsQ.Right := Item^.ExistsQ.Right ;
Item^.ExistsQ.Right^.ExistsQ.Left := Item^.ExistsQ.Left
END
END SubFromExists ;
(*
AddToSemaphore - adds item, Item, to the semaphore queue defined by Head.
*)
PROCEDURE AddToSemaphore (VAR Head: DESCRIPTOR; Item: DESCRIPTOR) ;
BEGIN
IF Head=NIL
THEN
Head := Item ; (* Head is empty therefore make *)
Item^.SemaphoreQ.Left := Item ; (* Item the only entry on this *)
Item^.SemaphoreQ.Right := Item (* queue. *)
ELSE
Item^.SemaphoreQ.Right := Head ; (* Add Item to the end of queue *)
Item^.SemaphoreQ.Left := Head^.SemaphoreQ.Left ;
Head^.SemaphoreQ.Left^.SemaphoreQ.Right := Item ;
Head^.SemaphoreQ.Left := Item
END
END AddToSemaphore ;
(*
AddToSemaphoreExists - adds item, Item, to the semaphore exists queue.
*)
PROCEDURE AddToSemaphoreExists (Item: SEMAPHORE) ;
BEGIN
IF AllSemaphores=NIL
THEN
AllSemaphores := Item ; (* Head is empty therefore make *)
Item^.ExistsQ.Left := Item ; (* Item the only entry on this *)
Item^.ExistsQ.Right := Item (* queue. *)
ELSE
Item^.ExistsQ.Right := AllSemaphores ;
(* Add Item to the end of queue *)
Item^.ExistsQ.Left := AllSemaphores^.ExistsQ.Left ;
AllSemaphores^.ExistsQ.Left^.ExistsQ.Right := Item ;
AllSemaphores^.ExistsQ.Left := Item
END
END AddToSemaphoreExists ;
(*
AddToReady - adds item, Item, to the ready queue.
*)
PROCEDURE AddToReady (Item: DESCRIPTOR) ;
BEGIN
AddToReadyQ(RunQueue[Item^.RunPriority], Item)
END AddToReady ;
(*
AddToReadyQ - adds item, Item, to the ready queue defined by Head.
*)
PROCEDURE AddToReadyQ (VAR Head: DESCRIPTOR; Item: DESCRIPTOR) ;
BEGIN
IF Head=NIL
THEN
Head := Item ; (* Head is empty therefore make *)
Item^.ReadyQ.Left := Item ; (* Item the only entry on this *)
Item^.ReadyQ.Right := Item (* queue. *)
ELSE
Item^.ReadyQ.Right := Head ; (* Add Item to the end of queue *)
Item^.ReadyQ.Left := Head^.ReadyQ.Left ;
Head^.ReadyQ.Left^.ReadyQ.Right := Item ;
Head^.ReadyQ.Left := Item
END
END AddToReadyQ ;
(*
SubFromReady - subtract process descriptor, Item, from the Ready queue.
*)
PROCEDURE SubFromReady (Item: DESCRIPTOR) ;
BEGIN
SubFromReadyQ(RunQueue[Item^.RunPriority], Item)
END SubFromReady ;
(*
SubFromReadyQ - removes a process, Item, from a queue, Head.
*)
PROCEDURE SubFromReadyQ (VAR Head: DESCRIPTOR; Item: DESCRIPTOR) ;
BEGIN
IF (Item^.ReadyQ.Right=Head) AND (Item=Head)
THEN
Head := NIL
ELSE
IF Head=Item
THEN
Head := Head^.ReadyQ.Right
END ;
Item^.ReadyQ.Left^.ReadyQ.Right := Item^.ReadyQ.Right ;
Item^.ReadyQ.Right^.ReadyQ.Left := Item^.ReadyQ.Left
END
END SubFromReadyQ ;
(*
SubFromSemaphoreTop - returns the first descriptor in the
semaphore queue.
*)
PROCEDURE SubFromSemaphoreTop (VAR Head: DESCRIPTOR) : DESCRIPTOR ;
VAR
Top: DESCRIPTOR ;
BEGIN
Top := Head ;
SubFromSemaphore(Head, Top) ;
RETURN( Top )
END SubFromSemaphoreTop ;
(*
SubFromSemaphore - removes a process, Item, from a queue, Head.
*)
PROCEDURE SubFromSemaphore (VAR Head: DESCRIPTOR; Item: DESCRIPTOR) ;
BEGIN
IF (Item^.SemaphoreQ.Right=Head) AND (Item=Head)
THEN
Head := NIL
ELSE
IF Head=Item
THEN
Head := Head^.SemaphoreQ.Right
END ;
Item^.SemaphoreQ.Left^.SemaphoreQ.Right := Item^.SemaphoreQ.Right ;
Item^.SemaphoreQ.Right^.SemaphoreQ.Left := Item^.SemaphoreQ.Left
END
END SubFromSemaphore ;
(*
Idle - this process is only run whenever there is no other Runnable
process. It should never be removed from the run queue.
*)
PROCEDURE Idle ;
VAR
ToOldState: PROTECTION ;
BEGIN
ToOldState := TurnInterrupts(MIN(PROTECTION)) ; (* enable interrupts *)
LOOP
(*
Listen for interrupts.
We could solve chess endgames here or calculate PI etc.
We forever wait for an interrupt since there is nothing else
to do...
*)
ListenLoop
END
(* we must NEVER exit from the above loop *)
END Idle ;
(*
InitIdleProcess - creates an idle process descriptor which
is run whenever no other process is Runnable.
The Idle process should be the only process which
has the priority idle.
*)
VAR
IdleProcess: DESCRIPTOR ; (* Idle process always runnable *)
PROCEDURE InitIdleProcess ;
VAR
db : ARRAY [0..80] OF CHAR ;
BEGIN
NEW(IdleProcess) ;
WITH IdleProcess^ DO
ALLOCATE(Start, IdleStackSize) ;
Size := IdleStackSize ;
NEWPROCESS(Idle, Start, IdleStackSize, Volatiles) ;
InitQueue(SemaphoreQ) ; (* not on a semaphore queue *)
Which := NIL ; (* at all. *)
StrCopy('Idle', RunName) ; (* idle process's name *)
Status := Runnable ; (* should always be idle *)
RunPriority := idle ; (* lowest priority possible *)
Debugged := FALSE ; (* should never be debugging *)
END ;
AddToReady(IdleProcess) ; (* should be the only *)
(* process at this run priority *)
AddToExists(IdleProcess) (* process now exists.. *)
END InitIdleProcess ;
(*
InitInitProcess - creates a descriptor for this running environment
so it too can be manipulated by Reschedule.
This concept is important to understand.
InitInitProcess is called by the startup code to this
module. It ensures that the current stack and processor
volatiles can be "housed" in a process descriptor and
therefore it can be manipulated just like any other
process.
*)
PROCEDURE InitInitProcess ;
BEGIN
NEW(CurrentProcess) ;
WITH CurrentProcess^ DO
Size := 0 ; (* we dont know the size of main stack *)
Start := NIL ; (* we don't need to know where it is. *)
InitQueue(ReadyQ) ; (* assign queues to NIL *)
InitQueue(ExistsQ) ;
InitQueue(SemaphoreQ) ; (* not waiting on a semaphore queue yet *)
Which := NIL ; (* at all. *)
StrCopy('Init', RunName) ; (* name for debugging purposes *)
Status := Runnable ; (* currently running *)
RunPriority := lo ; (* default status *)
Debugged := FALSE ; (* not deadlock debugging yet *)
END ;
AddToExists(CurrentProcess) ;
AddToReady(CurrentProcess)
END InitInitProcess ;
(*
InitQueue - initializes a queue, q, to empty.
*)
PROCEDURE InitQueue (VAR q: DesQueue) ;
BEGIN
WITH q DO
Right := NIL ;
Left := NIL
END
END InitQueue ;
(*
Init - initializes all the global variables.
*)
PROCEDURE Init ;
BEGIN
ExistsQueue := NIL ;
RunQueue[lo] := NIL ;
RunQueue[hi] := NIL ;
RunQueue[idle] := NIL ;
AllSemaphores := NIL ;
GarbageItem := NIL ;
InitInitProcess ;
InitIdleProcess
END Init ;
BEGIN
Init
END Executive.