Page Numbers: Yes First Page: 1
Heading:
UNPUBLISHED WORKING DRAFT - DO NOT COPY OR DISTRIBUTE
January 2, 1978 11:41 AM 5: Processes [IFS]<KRL>specs>process.bravo
5. Signals, Processes and schedulers
This file contains descriptions of the signal mechanism for KRL-1. It also contains a description of the process creation and scheduling mechanisms for the goal-driven part of programming. The syntax for procedural attachment is described in section 4 on conversion, along with a list of probes made by the system. The approach in KRL-1 to both signals and process management is similar to that of KRL-0, although generalized and sanitized.
5.1 Signal Tables
Procedural parameterization in KRL-1 is done through a signal mechanism similar to that of KRL-0. A signalTable is a list of signalResponses, each consisting of a signalName followed by a list of actions. On a call to
(Signal signalName signalTable OKForNoAction)
if a response keyed by signalName is found in signalTable, the list of actions is performed. Each action can be one of the following:
a standard response OK, SKIP, STOP, or ABORT;
a function name (an atom other than a standard response);
a form for evaluation.
If the action is a standard response, that atom is returned as the value of the action. If the action is any other atom, it is applied as a function of no arguments. Otherwise the form is evaluated. The value of the last action is returned as the value of
Signal. Any function which calls Signal is expected to be able to handle any of the standard responses. The intended meaning of each is as follows:
OKGo on with computation
SKIP
Skip this local computation, but go on in the larger context
STOP
Stop computation in larger computation, normal exit
ABORT
Stop computation in larger computation, abnormal exit
The most significant difference between Signal in KRL-1 and the one used in KRL-0 is that there is no longer a dynamically bound signal path which is searched for a signal response. Instead, each call to Signal requires that the user specify one table in which the expected response is to appear. If no response is found in that table, then the action taken depends on the the value of the flag OKForNoAction. If OKForNoAction = T then Signal immediately returns OK. If OKForNoAction = NIL then DefaultSignalTable is searched; if no response is found in the table bound to that variable, then KHELP is called.
The data structure used to represent a signalTable is an association list. Therefore to add a new response, or replace an old response, in a previously existing signalTable, one need simply use CONS. For example, the following fragment would cause SimpleMatchTable to have a new local binding with the response to the signal FollowCoreferences causing a special function call to TallyRefs to be made before execution:
(PROG((SimpleMatchTable
<<’FollowCoreferences TallyRefs OK> ! SimpleMatchTable>))
...)
In order to facilitate modification of already existing responses, there is a function:
(NewActionForSignal signalName action signalTable actionListFlag)
which returns a signal table which is signalTable with one new response on the front. This new response consists of the action action followed by any previous actions for that signal in signalTable. If actionListFlag = T then action is treated as a list of actions to prefix the previous action list. The new signal table shares as much structure as possible with the signalTable given.
5.2 Basic process objects
The process mechanism uses two fundamental data structures: Process and Scheduler. A Process (which is like a call in KRL-0) may be on any number of agendas (0,1, or more) [currently, it may not appear on the same agenda at more than one place in its list of processes]. A scheduler is a structure combining declarative information about the state of the agenda, with procedures (LISP functions and expressions) to be used for adding, removing, and running processes. Various kinds of scheduling regimes are provided and hooks exist for the user to specify his own.
Process
name: A LISP atom; this field has no semantic significance for the Process code itself and is provided only as a convenience; however, if this process is created through a call To GenRec or CreateNamedRec, then any of the process functions that can take a Process as an argument can also take the name of the process (which will then be used on a key to the hash table NamedRecords).
status: One of the atoms NEW, RUNNING, SUSPENDED, DEAD. It is NEW before the process has ever been run, RUNNING while it actually has control, DEAD if it has been terminated or run to completion, SUSPENDED otherwise. Note that more than one process in the system can be running, since a process which calls RunScheduler has status RUNNING during the time anything on the agenda of the scheduler specified is running. Furthermore, if a process passes off control by itself (e.g. by a RESUME) its status remains RUNNING.
description: A handle to an anchor containing a description of the process, to be used by whoever for whatever. This would contain resource information, or any other kind of user-desired stuff.
expression: A LispExpression which is to be evaluated when I run. This is used if no continuation is present. Note that it will be evaluated in the environment of the scheduler, not the environment in which the Process record was created.
function: A LISP function (atomic name or lambda expression) to be applied to the arguments when I run. This is used if neither a continuation nor expression is present.
arguments: A LISP list to which the function will be APPLYed.
terminationAction: A LispExpression to be evaluated if I am to be abnormally terminated (for things like failure, abort-interrupt a la shift-swat, etc.). Note that it will be evaluated in the environment of the scheduler, not the environment in which the Process record was created. "Abnormal" is from the perspective of the code in that it means that the expression or function did not run to completion. Actually, it is expected that TerminateProcess will be the preferred means of terminating running processes rather than KillProcess; TerminateProcess evaluates the process’s terminationAction then does a KillProcess.
value: Processes get values when they are killed (and thus when terminated). The value is the value of the expression or function evaluated if that returned normally or the value specified in the call to KillProcess (and thus TerminateProcess) that killed this process (’KILLED is the default).
schedulers: A List of the schedulers on whose agendas I appear. This is used by RemoveFromAllAgendas so the user should do all addings to and removings from agendas with the process functions or else understand what they do.
invokingScheduler: The scheduler which most recently caused me to run.
continuation: A LispStackPointer to be RESUMEd when I run.
invocation: stack pointer to the place in the RunScheduler code where this (running) process was invoked; intended essentially for internal use by the Process code.
Scheduler
name: A LISP atom; this field has no semantic significance for the Process code itself and is provided only as a convenience; however, if this process is created through a call To GenRec or CreateNamedRec, then any of the process functions that can take a Scheduler as an argument can also take the name of the Scheduler (which will then be used on a key to the hash table NamedRecords).
agenda: A list of Processes.
currentProcess: The Process currently being run by this scheduler.
adder: A Lisp function of two arguments which are bound to the new process and the scheduler (in that order). It should return as its value the new agenda (it must make the actual change to the agenda as well, but the value returned may be used by whoever called AddProcessToAgenda).
remover: A function of two arguments which are bound to the process and the scheduler (in that order). It must do whatever actions are desired to the agenda. The value returned should be the new agenda.
selecter: A function of one argument which is bound to the scheduler on which it appears. It must return as its value the process to be run. Any changes to the agenda (e.g. removing this process) must be handled by the selecter. The function does not need to set currentProcess, as this will be done automatically.
lastProcessValue: The LISP value returned by the last process run by this scheduler [not yet implemented]
beforeProcessAction: A function of one argument which is bound to the scheduler on which it appears. It will be evaluated after a new currentProcess has been established, but before it is run. This enables the user to put in additional actions while using a common scheduler. This is where resource checking could go. It also provides a hook for testing for "done" conditions; simply (RETFROM ’RunScheduler) or do something to the invokingProcess (Suspend, Kill, Reschedule) if specified condition is met.
afterProcessAction: Just like beforeProcessAction, except happens just after the call to RunProcess.
exitAction: A function of one argument which is bound to the scheduler on which it appears. It will be evaluated if the scheduler is run and the agenda is empty.
instanceVars: A field provided for the various functions associated with this scheduler to keep information specific to this scheduler in, as for instance resource allocations.
invokingProcess: value of CURRENTPROCESS when RunScheduler was called; in other words, the Process the code for which resulted in running this scheduler.
lastProcessValue: The LISP value returned by the last process run by this scheduler [not yet implemented? Does this seem a useful thing to have now?]
5.3 The scheduling framework
The function call (RunScheduler scheduler) is used to run a Scheduler. Its argument must be a valid scheduler record or an atom which hashes to a valid scheduler record in the hash table NamedRecords. RunScheduler can be called directly in LISP, or put into the function field of a Process record like any other function. It loops through the following steps indefinitely (until explicit giving up of control by one of the actions, or there are no more processes on the agenda):
bind the variable CURRENTSCHEDULER, giving it the value of the argument to RunScheduler.
if CURRENTSCHEDULER:agenda is NIL, then execute CURRENTSCHEDULER:exitAction. If it returns control, return its value (or NIL if there was no exitAction) as the value of RunScheduler.
otherwise
select a process by applying scheduler:selecter to CURRENTSCHEDULER, using DefaultSelecter (take first of agenda, and remove it from agenda) if scheduler:selecter is NIL. If the selecter returns NIL signal SelecterFailed.
execute beforeProcessAction, if any.
call RunProcess giving it the newly selected process as its argument.
execute afterProcessAction, if any.
The function call (RunProcess process) is used to run a process. Its argument must be a valid process record or an atom which hashes to a valid process record in the hash table NamedRecords. RunProcess is not normally invoked by the user, but rather is internal to the RunScheduler function. However, the user might want to invoke it in special circumstances. Its action is:
bind the variable CURRENTPROCESS ← the argument to RunProcess
CURRENTSCHEDULER:currentProcess ← CURRENTPROCESS
if process:status is:
DEAD --> signal ScheduledDeadProcess
RUNNING --> signal ProcessAlreadyRunning
NEW or SUSPENDED --> set status of process to RUNNING
set CURRENTPROCESS:invokingScheduler to CURRENTSCHEDULER and set CURRENTPROCESS:invocation appropriately.
if the CURRENTPROCESS is SUSPENDED, RESUME it
if it is NEW, then
if it has a function and arguments, apply the function to the arguments
otherwise if it has an expression, EVAL it
otherwise signal UnrunnableProcess
if control returns, then (KillProcess CURRENTPROCESS value-returned)
5.4 Functions for manipulating processes and schedulers
(RunScheduler Scheduler)
See definition above in description of basic scheduler actions.
(RunProcess Process)
See definition above in description of basic scheduler actions.
NOTE: In the following functions, if the argument Process is NIL then CURRENTPROCESS is used; if the argument Scheduler is NIL then CURRENTSCHEDULER is used.
(AddToAgenda Process Scheduler)
Apply the adder from Scheduler. Value is the new agenda. If adder is NIL, then use DefaultAdder, which essentially just adds the new Process onto the end of Scheduler:agenda.
(RemoveFromAgenda Process Scheduler)
Apply the remover from Scheduler. Value is the new agenda. If remover is NIL, then use DefaultRemover, which essentially does a DREMOVE.
(RemoveFromAllAgendas process)
Value is the process itself. This is equivalent to:
(for x in Process:schedulers
do (RemoveFromAgenda Process x)
finally (RETURN Process))
(SuspendProcess Process)
Status of Process must be RUNNING. It is changed to SUSPENDED, a continuation put into Process:continuation, and control returned to Process:invokingScheduler. [It is not yet clear what all the implications are of suspending anyone but yourself.] SuspendProcess doesn’t put the process back on an agenda. Value returned is the process.
(Reschedule Process Scheduler)
Equivalent to (AddToAgenda Process Scheduler) followed by a (SuspendProcess Process).
Typical use is (Reschedule), meaning Reschedule me.
(KillProcess Process Value)
Status of process is set to DEAD. Control returns to the place in the call to RunScheduler that resulted in this process being run, as specified by the stack pointer stored in Process:invocation. The dead process is removed from the agendas it is on,. [It is not yet clear what all the implications are of killing anyone tother than yourself.] Value returned is (OR Value ’KILLED).
(TerminateProcess Process Value)
The terminationAction (if any) of the process is eval’d in the control context of the caller. Then, (KillProcess Process (OR Value {value-of terminationAction})).
(Yield)
Signals Yield. If response is SKIP, then simply returns. If response is OK then does (Reschedule). Meant to be used as a check of resources and continuation if OK. Value returned is OK if no suspension, value of the resume otherwise.
5.5 Signals Generated by the Process Functions
This section contins a list of all the signals that may be generated by the process code and an explanation of the action resulting from the standard Signal responses (i.e., OK, SKIP, STOP, ABORT). The process functions all signal using the variable ProcessSignalTable, which should therefore be directly altered, reset, or rebound by the user to affect the response to the process signals.
There are three classes of signals in the process code (this classification would probably be applicable to any other piece of KRL system code too): notifications, warnings, and errors. The first kind are issued to provide hooks for user actions and in general do not look at the values returned by Signal. The second group are warnings of potential problems that can be ignored with no ill effects. The last class of signals indicate error conditions where some user action must be taken for processing to procede.
A. Hooks
AddingProcessToAgenda (adders)
RemovingProcessFromAgenda (removers)
SuspendingProcess (SuspendProcess)
KillingProcess (KillProcess)
Yield (Yield) return SKIP to continue Process calling Yield, OK to Reschedule
B. Warnings
A convention is observed for these signals with regard to values returned by the signal table:
OK:go on (don’t print any message)
SKIP:print a warning message and continue
STOP:print a warning message and break, showing relevant variables (usually the arguments for the process and scheduler involved), and enter a KHELP which should be returned from via SKIP (meaning don’t do operation) or OK (meaning go on).
ProcessAlreadyOnAgenda (adders)
ProcessNotOnAgenda (removers)
ProcessNotRunning (SuspendProcess, KillProcess)
ProcessDead (RunProcess)
ProcessNotRunnable (RunProcess)
SchedulingDeadProcess (AddToAgenda)
SchedulingAlreadyRunningProcess (AddToAgenda)
C. Errors
SelecterFailed (RunScheduler)
UnrunnableProcess (RunProcess)
UnresumeableProcess (RunProcess)
5.6 Miscellaneous
(KRL CONTINUEFLG)
To start KRL
CONTINUEFLG=T:continue, i.e. do not restore agendas, etc.
As in KRL-0, you are put in a user executive loop and are expected to add processes to the KrlTopLevelScheduler (normally with the AddToAgenda command -- CURRENTSCHEDULER is bound to KrlTopLevelScheduler at this point). To quit, surrendering control to KrlTopLevelScheduler, type OK.
(CreateNamedRec Type Name)
Creates a record of type Type. If that record had a field called name then set it to Name. Index the record under Name in the hash table NamedRecords.
(GetNamedRec Name)
(GETHASH Name NamedRecords).
(GenRec Type)
Adds 1 to the GENNUM property of Type (0 if none). Sets the GENNUM property of Type to this new integer. Creates a record of type Type with name the concatenation of Type and the new integer. E.g., (GenRec ’Process) will call CreateNamedRec with arguments ’Process and ’Process0 the first time, ’Process and ’Process1 the next, etc.
(PrintRec Rec RECNAME PRINTLEV FIL INDENT)
Names and prints the value of each non-NIL field of Rec. RECNAME is the name of the record declaration to use; if NIL, assume Rec is a typerecord and use its CAR. PRINTLEV governs the printlevel; default is 2. INDENT is number of spaces to indent each line; default is 4. FIL is the file to print to. Value returned is the name of the record used, i.e., (OR RECNAME (CAR Rec)).
(PrintNamedRec Name DECL PRINTLEV FIL INDENT)
Equivalent to (PrintRec (Getnamed Rec Name) DECL PRINTLEV FIL INDENT)).
ShowProcess, ShowScheduler, ShowAgenda
Take arguments, NIL, a record (type Process for the first, Scheduler for the other two), or an atom. NIL is equivalent to giving CURRENTPROCESS or CURRENTSCHEDULER as an argument; an atom is used as a key into the hash table NamedRecords. They print the requested structure appropriately on the terminal.
There are also various functions associated with the display monitoring system, but these are documented elsewhere.