First off, what I'm doing is non-standard (it uses multiprocessing). I have a
loop which runs forever. I would like to be able to break out of it at any
time, execute whatever protected forms were necessary, and then get out of
there. But I want to be able to do it from a remote part of the Lisp world
(i.e. a totally separate thread). Basically, like throw and catch, but not so
strict on the lexical/dynamic restrictions.
The thing that might be missing from my understanding is "environment". I
never understood exactly how environments are used in Lisp. If this is the key
to what I'm missing, I'd appreciate help.
dave
I do this all the time. here's how. in process A, you have:
(catch 'interrupt
(loop ...))
you need a function INTERRUPT
(defun interrupt (&optional value)
(ignore-errors (throw 'interrupt value)))
now you can do
(process-interrupt <process-A> #'interrupt <value>)
assuming, of course, you have something like Symbolics' multiprocessing,
such as supported by Allegro CL. note that INTERRUPT actually executes
in process A, i.e., inside the proper dynamic environment. (you want the
IGNORE-ERRORS because you have no idea what process A is doing when it is
asked to run this function, and you might execute entirely unexpected
error handlers if you try to throw to a tag not in scope at the time.)
#:Erik
--
@1999-07-22T00:37:33Z -- pi billion seconds since the turn of the century
> I do this all the time. here's how. in process A, you have:
>
> (catch 'interrupt
> (loop ...))
>
> you need a function INTERRUPT
>
> (defun interrupt (&optional value)
> (ignore-errors (throw 'interrupt value)))
>
> now you can do
>
> (process-interrupt <process-A> #'interrupt <value>)
>
> assuming, of course, you have something like Symbolics' multiprocessing,
> such as supported by Allegro CL. note that INTERRUPT actually executes
> in process A, i.e., inside the proper dynamic environment. (you want the
> IGNORE-ERRORS because you have no idea what process A is doing when it is
> asked to run this function, and you might execute entirely unexpected
> error handlers if you try to throw to a tag not in scope at the time.)
It's a little more elegant if you use restarts instead of catch/throw,
though the effect is the same. (Restarts are implemented with catch/throw,
after all.) The difference is that restarts offer an introspective
mechanism that allows you to first determine that the throw tag [restart]
is not present and not to try throwing. That means you don't have to
do the throw in an IGNORE-ERRORS, and it also allows you to choose among
several possible return points in more elaborate situations.
(defun compute-something-interruptible ()
(with-simple-restart (dismiss "Dismiss pending computation.")
(loop ...)))
(defun interrupt ()
(let ((r (find-restart 'dismiss)))
(when r (invoke-restart r))))
(process-interrupt <process-to-interrupt> #'interrupt)
It can be done with values, too, but I did it without because I'm in a
hurry to rush out the door and anyway I thought it would make the
shape of the code clearer. To use values, you just use invoke-restart
with an extra arg and change the with-simple-restart to a restart-case.
The syntax for restart-case is a lot more general and is documented
in CLHS.
thanks, very much
dave