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.