REF PROCESS John Gibson Jul 1995
COPYRIGHT University of Sussex 1995. All Rights Reserved.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< PROCESSES >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
A process in Poplog is a data structure that records the state of
execution of a piece of Poplog program (see also HELP * PROCESS). This
file describes processes, and procedures to construct and operate on
them.
CONTENTS - (Use <ENTER> g to access required sections)
1 Introduction
2 Predicates on Processes
3 Constructing Processes
4 Running, Suspending and Resuming
5 Generic Data Structure Procedures on Processes
6 Miscellaneous
7 Example
---------------
1 Introduction
---------------
A process in Poplog is a data structure that records the state of
execution of a piece of Poplog program. The information stored in a
process record comprises the sequence of procedure calls (stack frames)
that the process is currently inside (including the values of local
variables of those procedures), and the state of the user stack.
A process is constructed initially in two ways:
¤ from a procedure with consproc, in which case, on running it for
the first time with runproc (or resume), the procedure is called
in the normal way;
¤ from part of the currently active procedure calls with
consproc_to, in which case execution continues inside the process
after the call to consproc_to.
Thereafter, the process may suspend itself at any time, e.g. by using
suspend. This causes the current state of execution (all procedure calls
upto runproc and the user stack) to be stored in the original process
record -- the process is then 'swapped out'. The process may then be
re-activated with runproc and will continue execution immediately
following the suspend call, after the stored state of execution has been
reinstated.
A process can also cause itself to be swapped out by calling resume
to resume another process in its place (see below). There are also
versions of suspend and resume (ksuspend and kresume) which 'kill' the
current process. This means that the process' state is swapped out but
not stored, the process record being marked as 'dead'. The process
cannot then be run again. (A process is also killed if it does a normal
procedure exit to runproc.)
Note that a process always has its own user stack, which is separate
from the stack of any other process or from the normal stack when not
running inside a process. Thus all arguments passed into or out of a
process have to be explicitly declared in calls of runproc, suspend,
etc. The exception to this is when a process does a normal procedure
exit to runproc, or exits abnormally through runproc with chain or
related procedures: in this case all values on the process's stack are
passed up as results (thus if you leave items on the stack in a process
which you do not want passed back on normal or chained exit, use
clearstack to clear the stack first).
Processes can be used hierarchically, i.e. one process can be run as
a sub-process of another. While procedures like suspend which suspend a
process normally apply only to the current process, they can all take an
optional process argument specifying any running process to be
(k)suspended. This means that all processes up to and including the one
specified are either killed (ksuspend and kresume), or suspended in such
a way that on running or resuming the outer process the whole process
chain is reactivated, and control returns from the original call
(suspend and resume). A process chain like this is also constructed by
consproc_to if the calling sequence to the target procedure includes one
or more processes; when the constructed process is run it will
reactivate the whole chain.
Sub-processes that get suspended when suspending an outer process
are said to be 'subsumed'. Subsumed processes cannot be run again in
their own right, but only by reactivating the outer process which
subsumed them.
--------------------------
2 Predicates on Processes
--------------------------
isprocess(item) -> bool [procedure]
Returns true if item is a process, false if not.
isliveprocess(item) -> result [procedure]
Returns a true result if item is a live process, and false
otherwise (i.e. if not a process, or a dead one). In the first
case, the return is item itself if item is a currently running
process, or true if the process is suspended. Thus
isliveprocess(proc) == proc
can be used to test whether a process proc is running.
-------------------------
3 Constructing Processes
-------------------------
A process is either volatile or non-volatile. With a volatile process,
the saved state held in the process record is lost when the process is
run, so that the record is effectively empty until such time as the
process suspends again. With a non-volatile process, the saved state (as
it was at the time of running) is retained until the process is next
suspended.
Not retaining the saved state means that data structures which a
running process was using at the time of its being run, but which it no
longer requires, will be garbage collected -- thus a volatile process is
more garbage-collection efficient. On the other hand, if a running
process is abnormally exited due to an error condition, the system has
no choice but to kill that process unless it retains a sensible saved
state; thus a volatile process will always be killed in these
circumstances, but a non-volatile one will survive (providing the error
does not actually occur in the middle of the process being suspended,
since starting to suspend corrupts the old saved state).
Therefore, use a non-volatile process only if you require to retain
the old state while the process is running.
consproc(item1, ..., itemN, N, p) -> proc [procedure]
consproc(item1, ..., itemN, N, p, volatile) -> proc
The result of this procedure is a process constructed on the
procedure p, with its initial user stack set to contain N items
passed from the current stack. When the process is first run
(with runproc, resume or kresume), the procedure p will be
called with those items on the stack, i.e. the process will
commence with:
p(item1, ..., itemN)
The new process will die when the given procedure returns.
volatile is an optional boolean argument specifying whether the
process is volatile or not (see above). If not specified the
default is true, i.e. volatile.
consproc_to(item1, ..., itemN, N, target_p) -> proc [procedure]
consproc_to(item1, ..., itemN, N, target_p, volatile) -> proc
Makes the current calling sequence upto and including the the
most recent call of the procedure target_p into a process,
returning the process record proc for the new (running) process.
This procedure effectively 'inserts' a call of runproc
immediately above the call of target_p, does a suspend, and then
immediately runs the process again with proc passed as argument.
The implicit suspend automatically subsumes any processes
running below the call of target_p; these will be reactivated
when proc is run again.
The user stack of proc is then set to contain N items passed
from the current stack (i.e. the stack as it is AFTER suspending
intervening processes). To allow computation (in that
environment) of the number of items to be passed, the argument N
may also be a procedure which returns the number, i.e. N() is
evaluated after suspending intervening processes.
The argument volatile (and its default) are as for consproc.
saveproc() -> proc [procedure]
Makes a copy of the state of the current process; the result is
a process record proc which when run will exit from the call of
saveproc with false as result instead of the process record.
Thus saveproc should be used in something like
if saveproc() ->> proc then
;;; copy returned, running in original
else
;;; running in copy
endif
Saving the state involves suspending the current process and
then copying it using copy (see Generic Datastructure Procedures
on Processes below). The original is then run again with the
copy passed as argument. To allow the copy to be run with
arguments, saveproc is defined roughly as follows:
define saveproc();
lconstant mark = 'mark';
suspend_chain(pop_current_process, 1,
procedure(proc);
lvars proc;
chain(copy(proc), mark, 2, proc, runproc)
endprocedure);
if stacklength() /== 0 and dup() == mark then
;;; running original
;;; erase mark leaving copy on stack as result
->
else
;;; running copy
;;; return false above any other arguments
false
endif
enddefine;
-----------------------------------
4 Running, Suspending and Resuming
-----------------------------------
pop_current_process -> proc_or_false [variable]
proc_or_false -> pop_current_process
Always contains the current process, or false if no processes
are running. You can thus use
pop_current_process == proc
to test if proc is the current process.
runproc(item1, ..., itemN, N, proc) [procedure]
Runs the process proc as a subprocess of the current process (or
from outside any process), passing N items from the current user
stack to the process user stack. (If runproc is used inside a
process, the calling process is not 'swapped out'. All calls of
the outer process remain in the calling chain, BUT the outer
process user stack is saved (somewhere) so the called process
still runs with its own stack.)
Since runproc is the class_apply of processes, this can also be
called as
proc(item1, ..., itemN, N)
suspend(item1, ..., itemN, N) [procedure]
suspend(item1, ..., itemN, N, sus_proc)
Suspends the current process (first form), or all processes upto
and including the process sus_proc (second form), and returns
from the call of runproc which ran the suspended process. N
items are passed back as results from the current user stack.
When the suspended process is run again (with runproc, resume or
kresume), the call of suspend will return with whatever items on
the stack were passed to it by the initiating procedure.
suspend_chain(item1, ..., itemN, N, chain_p) [procedure]
suspend_chain(item1, ..., itemN, N, sus_proc, chain_p)
Same as suspend, but chains the procedure chain_p out the call
of runproc which ran the process, instead of just returning from
it. (suspend is thus equivalent to suspend_chain with identfn
for the chain_p argument.)
resume(item1, ..., itemN, N, res_proc) [procedure]
resume(item1, ..., itemN, N, sus_proc, res_proc)
Suspends the current process (first form), or all processes upto
and including the process sus_proc (second form), and then runs
the process res_proc inside the call of runproc which was
running the suspended process (thus the newly-resumed process
replaces the suspended one). N items are passed from the current
user stack to the resumed process user stack.
When the suspended process is run again (with runproc, resume or
kresume), the call of resume will return with whatever items on
the stack were passed to it by the initiating procedure.
ksuspend(item1, ..., itemN, N) [procedure]
ksuspend(item1, ..., itemN, N, kill_proc)
Kills the current process (first form), or all processes upto
and including the process kill_proc (second form), and returns
from the call of runproc which ran the process. N items are
passed back as results from the current user stack.
ksuspend_chain(item1, ..., itemN, N, chain_p) [procedure]
ksuspend_chain(item1, ..., itemN, N, kill_proc, chain_p)
Same as ksuspend, but chains the procedure chain_p out the call
of runproc which ran the process, instead of just returning from
it. (ksuspend is thus equivalent to ksuspend_chain with identfn
for the chain_p argument.)
kresume(item1, ..., itemN, N, res_proc) [procedure]
kresume(item1, ..., itemN, N, kill_proc, res_proc)
Kills the current process (first form), or all processes upto
and including kill_proc (second form), and then runs the process
res_proc inside the call of runproc which was running the killed
process (thus the newly-resumed process replaces the killed
one). N items are passed from the current user stack to the
resumed process user stack.
-------------------------------------------------
5 Generic Data Structure Procedures on Processes
-------------------------------------------------
copy can be used to copy a process, but only if it has a runnable saved
state. This means that a volatile process can be copied only when
suspended; a non-volatile process can be copied either while suspended
or running (in the latter case the copy reflects the state as it was
when the process was run, i.e. the last time it was suspended).
After copying, the copy and the original process become completely
independent of each other.
(At least, this should be the case. Currently, the system is deficient
in that type-3 local lexical variables of procedures forming a process
are not copied, and may therefore give rise to unwanted interactions
between the copy and the original in programs using these (see
REF * VMCODE for a description of type-3 lvars); this bug will be fixed
in a future release of the system. Another current bug is that copying a
suspended process which has subsumed sub-processes does not copy the
subsumed processes, which it should.)
----------------
6 Miscellaneous
----------------
process_key -> key [constant]
This constant holds the key structure for process records (see
REF * KEYS).
----------
7 Example
----------
The following example creates a process proc which each time it is run
returns the next integer, starting from an initial value n:
define next(n);
lvars n;
repeat
suspend(n, 1);
n+1 -> n
endrepeat
enddefine;
vars proc = consproc(23, 1, next);
runproc(0, proc)=>
** 23
runproc(0, proc)=>
** 24
+-+ C.all/ref/process
+-+ Copyright University of Sussex 1995. All rights reserved.