i was once again hindered by this, so i digged deeper.

please find a test.scm attached.

looks like (R6RS) WITH-EXCEPTION-HANDLER has such a surprising semantics that i 
didn't even consider that to be the case. some hours later i found this:

https://practical-scheme.net/gauche/man/gauche-refe/Exceptions.html#index-with_002dexception_002dhandler

"Note: SRFI-18 specifies the exception handler installed with 
with-exception-handler will be called with exactly the same dynamic 
environment, including the exception handler settings. It means if an exception 
is raised within the handler, it will be caught with the same handler."

"However, the bare SRFI-18 semantics turned out to be error prone–users tended 
to assume errors from a handler would be handled by outer handler, and were 
perplexed when they ran into infinite recursion of handlers"

now, as surprising as the above was to me, guile behaves in a yet more 
surprising way, because seeing a stack overflow would have hinted the above 
semantics to me.

instead, what seems to be happening (?) is that there's some short-circuit 
somewhere in the guile codebase that aborts this infinite recursion by printing 
a backtrace and the exception... and then continues who knows where, and with 
what return value? unfortunately, it doesn't print anything else to explain the 
situation, or to grep for in the codebase.

context:
--------

in the original context of shepherd, it doesn't seem to just entirely exit the 
process, because shepherd continues to run, and IIRC it even functions to some 
extent. and it doesn't seem to just exit the current thread either, because 
IIUC shepherd is a single-threaded app (using fibers).

for reference, i'm including the output at the end of this mail that i'm seeing 
in my shepherd branch. note that it says "Uncaught exception in task:" which 
suggests that this exception seems to have left a fiber and reached fiber's 
RUN-SCHEDULER/ERROR-HANDLING.

and finally, my ultimate use-case is a CALL-WITH-ERROR-HANDLING function that 
does not unwind, and has three layers of nested error handlers to log any 
errors with a backtrace, including errors in the user supplied error handlers, 
or in the logging subsystem.


i see two actionables:
----------------------

if i'm not wrong in the above, then:

please add some message -- any message -- to this short-circuit code.

please extend the doc of WITH-EXCEPTION-HANDLER with a warning that explains 
the dynamic extent of the binding of the handler at the time the handler is 
called, and any possible short circuits installed in guile against that.

-- 
• attila lendvai
• PGP: 963F 5D5F 45C7 DFCD 0A39
--
“The strength of a person's spirit would then be measured by how much 'truth' 
he could tolerate, or more precisely, to what extent he needs to have it 
diluted, disguised, sweetened, muted, falsified.”
        — Friedrich Nietzsche (1844–1900), 'Beyond Good and Evil' (1886)



Uncaught exception in task:
In fibers.scm:
    172:8 13 (_)
In shepherd/service.scm:
    425:9 12 (_)
In ice-9/boot-9.scm:
  1752:10 11 (with-exception-handler _ _ #:unwind? _ # _)
In shepherd/service.scm:
   570:31 10 (service-controller #<service bee-1> #<<channel> getq: ?>)
In /gnu/store/0zd8bv4hmrfqhzb5qv6flslah0a7bplb-shepherd-bee-1.scm:
   1:3579  9 (_ _ . _)
In ice-9/boot-9.scm:
  1685:16  8 (raise-exception _ #:continuable? _)
  1752:10  7 (with-exception-handler _ _ #:unwind? _ # _)
In shepherd/support.scm:
    644:4  6 (log-with-backtrace _ #<&compound-exception componen?> . #)
In unknown file:
           5 (display-backtrace #<stack 7fee5677e9e0> #<output: str?> ?)
In system/repl/debug.scm:
   148:36  4 (print-frames #(#<frame 7fee564e3220 make-stack> #<?> ?) ?)
In ice-9/boot-9.scm:
   2137:6  3 (_)
  1747:15  2 (with-exception-handler #<procedure 7fee566a4780 at ic?> ?)
In system/repl/debug.scm:
    72:40  1 (_)
In ice-9/boot-9.scm:
  1685:16  0 (raise-exception _ #:continuable? _)
ice-9/boot-9.scm:1685:16: In procedure raise-exception:
In procedure string->number: Wrong type argument in position 1 (expecting 
string): #f
#!/usr/bin/env -S guile --no-auto-compile -e main -s
!#

(use-modules (srfi srfi-34))

(define* (test #:key (unwind? #f))
  (with-exception-handler
      (let ((nested #f))
        (lambda (c-level-1)
          (if nested
              (begin
                (format #t "level 1 handler got called recursively~%")
                'level-1-handler-nested)
              (begin
                (set! nested #t)
                (with-exception-handler
                    (lambda (c-level-2)
                      (begin
                        (format #t "level 2 handler got error ~A~%" c-level-2)
                        'level-2-handler))
                  (lambda ()
                    (format #t "level 1 handler~%")
                    (error "let's signal a nested error...")
                    (format #t "level 1 handler is returning~%")
                    'level-1-handler)
                  #:unwind? unwind?)))))
    (lambda ()
      (error "let's signal an error...")
      'thunk)
    #:unwind? unwind?))

(define (main cmd)
  (unsetenv "COLUMNS")
  (format #t "~%~%*** calling with unwind~%")
  (format #t "return value is: ~A~%" (test #:unwind? #t))
  (format #t "~%~%*** calling without unwind~%")
  (format #t "return value is: ~A~%" (test #:unwind? #f))
  (test #:unwind? #f))

Reply via email to