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);