Thanks Neil, that works. This is what I did: but I made no attempt to make
the C-level interface backwardly compatible. It seems the same option could
apply to scm_with_throw_handler, but I left that alone. Can anyone tell me
what where the semantics to the latter when the LAZY_THROW_P paramerter was
set? It sounds like it used to evaluate the handler in the dynamic context
of the destination, like a continuation.

Ian




On Wed, Aug 20, 2014 at 2:02 PM, Neil Jerram <n...@ossau.homelinux.net>
wrote:

> On 2014-08-18 03:14, Ian Grant wrote:
>
>> Hi Neil,
>>
>> Sorry, I am replying to my message because I'm not on guile-devel, it
>> seems. I'll join later.
>>
>> Thanks for clarifying that. The misleading statement in the manual is
>> just above the paragraph you quote: on
>> https://www.gnu.org/software/guile/manual/html_node/Catch.html#Catch
>> [2] it says
>>
>>
>>     Handler is invoked outside the scope of its own catch. If
>> handler
>>     again throws to the same key, a new handler from further up the
>>     call chain is invoked.
>>
>> It doesn't mention pre_unwind_handler, which implies that the
>> statement does _not_ apply to pre_unwind_handler.
>>
>
> Yes, I believe it is correct that those statements do not apply to the
> pre-unwind handler.
>
>
>  So this should be
>> amended.
>>
>> But it would be more useful if there were a way to allow a throw to be
>> aborted as I was expecting. How hard would that be to implement? If
>> you think the semantics are in fact different, it could be given a new
>> name, scm_c_catch_and_rethrow or something. What I want is something
>> like BSD signals semantics, where the handler is not reset when the
>> signal occurs.
>>
>> You ask "In your code, the pre-unwind handler calls failwith() and
>> doesn't expect it to return - so where does it jump to?"
>>
>> I thought I had explained that, sorry. The call to failwith() does a C
>> longjmp back into the CAML bytecode interpreter (i.e. back down below
>> the call stack into caml_main). This is not a non-local exit, as far
>> as Guile is concerned, because it is just a 'naked' C longjmp. So in
>> that sense, the second time Guile throws an exception it is being
>> thrown from within pre_unwind_handler.
>>
>
> OK, I think I see now.  From Guile's point of view, the dynamic context of
> the 'catch' remains in effect - as you want - but with a flag to say that
> the pre-unwind handler is already running (actually implemented by the
> %running-exception-handlers fluid in boot-9.scm).  That explains why the
> _next_ exception causes the catch's main handler to be called, instead of
> the pre-unwind handler again.
>
>
>  Is there a way to implement what I want with fluids?
>>
>
> I don't think so, no.  Basically what you need is to make some call, from
> the pre-unwind handler code, just before calling failwith(), that says
> "this pre-unwind handler should now be reactivated" and is implemented by
> removing itself from %running-exception-handlers.  But
> %running-exception-handlers is hidden inside a lexical environment, so that
> isn't possible without hacking the boot-9 code.
>
> You could try hacking the boot-9 code, though, to see if that idea works,
> and then propose a patch.
>
> Regards,
>      Neil
>
>
--- boot-9.scm	2014-02-14 18:00:33.000000000 -0400
+++ guile-2.0.11/module/ice-9/boot-9.scm	2014-08-22 03:31:22.296841040 -0400
@@ -92,13 +92,13 @@
             (apply abort-to-prompt prompt-tag thrown-k args)
             (apply prev thrown-k args)))))
 
-  (define (custom-throw-handler prompt-tag catch-k pre)
+  (define (custom-throw-handler prompt-tag catch-k pre pre-reset)
     (let ((prev (fluid-ref %exception-handler)))
       (lambda (thrown-k . args)
         (if (or (eq? thrown-k catch-k) (eqv? catch-k #t))
             (let ((running (fluid-ref %running-exception-handlers)))
               (with-fluids ((%running-exception-handlers (cons pre running)))
-                (if (not (memq pre running))
+                (if (or pre-reset (not (memq pre running)))
                     (apply pre thrown-k args))
                 ;; fall through
                 (if prompt-tag
@@ -107,7 +107,7 @@
             (apply prev thrown-k args)))))
 
   (set! catch
-        (lambda* (k thunk handler #:optional pre-unwind-handler)
+        (lambda* (k thunk handler #:optional pre-unwind-handler pre-reset)
           "Invoke @var{thunk} in the dynamic context of @var{handler} for
 exceptions matching @var{key}.  If thunk throws to the symbol
 @var{key}, then @var{handler} is invoked this way:
@@ -120,9 +120,18 @@
 @var{thunk} takes no arguments.  If @var{thunk} returns
 normally, that is the return value of @code{catch}.
 
-Handler is invoked outside the scope of its own @code{catch}.
-If @var{handler} again throws to the same key, a new handler
-from further up the call chain is invoked.
+@code{handler} is invoked outside the scope of its own @code{catch}.
+If @code{handler} again throws to the same key, a new handler from
+further up the call chain is invoked. @code{pre-unwind-handler} is
+invoked @emph{inside} the scope of the catch, in so far as if
+@var{key} is thrown from within the dynamic context of
+@code{pre-unwind-handler}, then @code{handler} will be invoked
+immediately, (i.e. without re-entering @code{pre-unwind-handler}. This
+behaviour can be changed by setting the optional argument
+@var{pre-reset} to @code{#t}, which makes throws within
+@code{pre-unwind-handler} re-enter the @code{pre-unwind-handler}. This
+allows exceptions to be cancelled in the @code{pre-unwind-handler}, by
+throwing to another handler in a different dynamic context.
 
 If the key is @code{#t}, then a throw to @emph{any} symbol will
 match this call to @code{catch}.
@@ -152,7 +161,7 @@
                (with-fluids
                    ((%exception-handler
                      (if pre-unwind-handler
-                         (custom-throw-handler tag k pre-unwind-handler)
+                         (custom-throw-handler tag k pre-unwind-handler pre-reset)
                          (default-throw-handler tag k))))
                  (thunk)))
              (lambda (cont k . args)
@@ -167,7 +176,7 @@
                          "Wrong type argument in position ~a: ~a"
                          (list 1 k) (list k)))
           (with-fluids ((%exception-handler
-                         (custom-throw-handler #f k pre-unwind-handler)))
+                         (custom-throw-handler #f k pre-unwind-handler #f)))
             (thunk))))
 
   (set! throw
--- continuations.c	2014-02-14 18:00:33.000000000 -0400
+++ guile-2.0.11/libguile/continuations.c	2014-08-22 05:50:43.126300151 -0400
@@ -455,7 +455,7 @@
   result = scm_c_catch (SCM_BOOL_T,
 			body, body_data,
 			handler, handler_data,
-			pre_unwind_handler, pre_unwind_handler_data);
+			pre_unwind_handler, pre_unwind_handler_data, 0);
 
   /* Return to old continuation root.
    */
--- load.c	2014-02-28 16:01:27.000000000 -0400
+++ guile-2.0.11/libguile/load.c	2014-08-22 05:51:03.282400099 -0400
@@ -863,7 +863,7 @@
                       SCM2PTR (source),
                       auto_compile_catch_handler,
                       SCM2PTR (source),
-                      NULL, NULL);
+                      NULL, NULL, 0);
 }
 
 /* The auto-compilation code will residualize a .go file in the cache
--- throw.c	2014-02-14 18:00:33.000000000 -0400
+++ guile-2.0.11/libguile/throw.c	2014-08-22 05:50:00.410088349 -0400
@@ -65,13 +65,13 @@
 
 SCM
 scm_catch_with_pre_unwind_handler (SCM key, SCM thunk, SCM handler,
-                                   SCM pre_unwind_handler)
+                                   SCM pre_unwind_handler, SCM pre_reset)
 {
   if (SCM_UNBNDP (pre_unwind_handler))
     return scm_catch (key, thunk, handler);
   else
-    return scm_call_4 (scm_variable_ref (catch_var), key, thunk, handler,
-                       pre_unwind_handler);
+    return scm_call_5 (scm_variable_ref (catch_var), key, thunk, handler,
+                       pre_unwind_handler, pre_reset);
 }
 
 static void
@@ -192,7 +192,7 @@
 scm_c_catch (SCM tag,
 	     scm_t_catch_body body, void *body_data,
 	     scm_t_catch_handler handler, void *handler_data,
-	     scm_t_catch_handler pre_unwind_handler, void *pre_unwind_handler_data)
+	     scm_t_catch_handler pre_unwind_handler, void *pre_unwind_handler_data, int pre_reset)
 {
   SCM sbody, shandler, spre_unwind_handler;
   
@@ -205,7 +205,7 @@
     spre_unwind_handler = SCM_UNDEFINED;
   
   return scm_catch_with_pre_unwind_handler (tag, sbody, shandler,
-                                            spre_unwind_handler);
+                                            spre_unwind_handler, scm_from_bool(pre_reset));
 }
 
 SCM
@@ -216,7 +216,7 @@
   return scm_c_catch (tag,
                       body, body_data,
                       handler, handler_data,
-                      NULL, NULL);
+                      NULL, NULL, 0);
 }
 
 
--- throw.h	2014-01-21 17:25:11.000000000 -0400
+++ guile-2.0.11/libguile/throw.h	2014-08-22 05:47:34.521364905 -0400
@@ -37,7 +37,7 @@
 			 scm_t_catch_handler handler,
 			 void *handler_data,
 			 scm_t_catch_handler pre_unwind_handler,
-			 void *pre_unwind_handler_data);
+			 void *pre_unwind_handler_data, int pre_reset);
 
 SCM_API SCM scm_c_with_throw_handler (SCM tag,
 				      scm_t_catch_body body,
@@ -76,7 +76,7 @@
 SCM_API SCM scm_handle_by_throw (void *, SCM, SCM);
 SCM_API int scm_exit_status (SCM args);
 
-SCM_API SCM scm_catch_with_pre_unwind_handler (SCM tag, SCM thunk, SCM handler, SCM lazy_handler);
+SCM_API SCM scm_catch_with_pre_unwind_handler (SCM tag, SCM thunk, SCM handler, SCM lazy_handler, SCM pre_reset);
 SCM_API SCM scm_catch (SCM tag, SCM thunk, SCM handler);
 SCM_API SCM scm_with_throw_handler (SCM tag, SCM thunk, SCM handler);
 SCM_API SCM scm_ithrow (SCM key, SCM args, int no_return);

Reply via email to