1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-02 13:00:26 +02:00
guile/devel/extension/dynamic-root.text
2001-12-04 15:10:16 +00:00

86 lines
3.9 KiB
Text

The Problem
===========
Certain applications embedding Guile (Scwm, Guppi) have found it
necessary to include hacked versions of scm_call_with_dynamic_root.
They want to run user callbacks, but don't want the callback to be
able to longjmp (via exceptions or continuations) randomly in and out,
since the C code hasn't been written to dynamically wind/unwind local
state. This is likely to be a common problem for users of Guile as an
extension language.
libguile/root.c:scm_call_with_dynamic_root seems to almost do this,
but it has the apparently undesirable behaviour of unwinding the
dynamic state when the protected procedure is called. In addition
the implementation looks a bit heavy for use in every callback.
scm_call_with_dynamic_root was implemented to support threading, so
the needs of libguile itself should be considered. Other
considerations are how any new interface interacts with error handling
and reporting; whether a new interface is convenient to use from C;
whether a new interface should also be available to Scheme code.
Discussion
==========
There are two ways that longjmp may be invoked from a Scheme callback:
raising an exception or invoking a continuation. Exceptions can be
caught using scm_internal_catch, so it could be argued that the new
interface only needs to block continuations.
However there are two problems with this: firstly it's unlikely that
anybody would want to block continuations without also catching
exceptions, so it's more convenient to use a single facility set up
both types of blocking. Secondly, the fact that exceptions and
continuations can be treated separately in Guile is just an
implementation detail: in general in Scheme it's possible to use
continuations to implement an exception mechanism, and it's
undesirable to tie a new language feature to an implementation detail
when it can be avoided, even at the C level.
Hence, the interface should take at least a) the callback to be
protected b) and exception handler and associated handler data to be
passed to scm_internal_catch.
On which side of the continuation barrier should be exception handler
be installed? Logically it belongs on the same side as the callback:
i.e., if the callback raises an exception then the handler can catch
it without crossing it the continuation barrier. But what happens if
the handler raises another exception? This doesn't seem like an
important concern, since the hander is under control of the code that
is trying to protect itself. It should be sufficient to warn in the
documentation that such exceptions produce undefined behaviour and
allow them to cross the continuation barrier.
How should the callback procedure be passed to the interface and
invoked? Should it be like scm_internal_catch where it's passed as a
C procedure (scm_t_catch_body) which is applied to user data (void *)?
For a procedure designed to be used from C, this is the most
convenient, since constructing closures in C is difficult. It also
gives symmetry with scm_internal_catch.
On the other hand, the body procedure is expected to be a Scheme
closure in most cases. This suggests implementing two C procedures,
the first taking four arguments:
scm_t_catch_body body, void *body_data,
scm_t_catch_handler handler, void *handler_data
and the second taking three arguments:
SCM body, scm_t_catch_handler handler, void *handler_data
If there is also to be a Scheme interface, then it would be implemented
with a third variant:
SCM body, SCM handler
The second and third variants would be implemented by calling the
first, similar to the old scm_call_with_dynamic_root and its wrappers.
The return value from all variants should be the result of calling
the body, unless an exception occurred in which case it's the result
of calling the handler. So the return type is SCM, as for
scm_internal_catch.
Yet to be discussed: libguile usage and threads, error handling and
reporting, convenience of use, Scheme-level interface.