octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

unwind_protect & try/catch combined corner case


From: John W. Eaton
Subject: unwind_protect & try/catch combined corner case
Date: Thu, 7 Jan 2010 14:06:09 -0500

On  7-Jan-2010, Jaroslav Hajek wrote:

| consider this script which mixes unwind_protect and try/catch in a
| not-really-neat way:
| unwind_protect
|   try
|     unwind_protect
|       fdisp (stderr, "press Ctrl-C");
|       pause (3)
|       #for i = 1:100000
|       #  fprintf (stderr, "\r%d", i);
|       #endfor
|     unwind_protect_cleanup
|       fprintf (stderr, "\n");
|       fdisp (stderr, "cleanup inner");
|       error ("raise error");
|     end_unwind_protect
|   catch
|     fdisp (stderr, "caught error");
|   end_try_catch
|   fdisp (stderr, "intermediate");
| unwind_protect_cleanup
|   fdisp (stderr, "cleanup outer");
| end_unwind_protect
| fdisp (stderr, "outer normal");
| 
| what should it do?

Since I copied this idea from Emacs Lisp, I decided to check to see
what Emacs would do.  In the Emacs Lisp manual, it says

 -- Special Form: unwind-protect body-form cleanup-forms...
     `unwind-protect' executes BODY-FORM with a guarantee that the
     CLEANUP-FORMS will be evaluated if control leaves BODY-FORM, no
     matter how that happens.  BODY-FORM may complete normally, or
     execute a `throw' out of the `unwind-protect', or cause an error;
     in all cases, the CLEANUP-FORMS will be evaluated.

     If BODY-FORM finishes normally, `unwind-protect' returns the value
     of BODY-FORM, after it evaluates the CLEANUP-FORMS.  If BODY-FORM
     does not finish, `unwind-protect' does not return any value in the
     normal sense.

     Only BODY-FORM is protected by the `unwind-protect'.  If any of
     the CLEANUP-FORMS themselves exits nonlocally (via a `throw' or an
     error), `unwind-protect' is _not_ guaranteed to evaluate the rest
     of them.  If the failure of one of the CLEANUP-FORMS has the
     potential to cause trouble, then protect it with another
     `unwind-protect' around that form.

     The number of currently active `unwind-protect' forms counts,
     together with the number of local variable bindings, against the
     limit `max-specpdl-size' (*note Local Variables: Definition of
     max-specpdl-size.).

So it should be expected that some care is needed if an error could
occur in the cleanup block.

For your example with an interrupt, I think it is OK if errors are not
handled after the interrupt.  But all cleanup blocks should be
executed in the proper sequence.  As you said in a later message, the
point of interrupt is to get back to the top-level as quickly as
possible while safely cleaning up along the way.

If your example is changed to have an error instead of an interrupt in
the first part of the unwind-protect block, like this:

  unwind_protect
    try
      unwind_protect
        error ("first error");
      unwind_protect_cleanup
        fprintf (stderr, "\n");
        fdisp (stderr, "cleanup inner");
        x = lasterror ();
        error ("raise error: %s", x.message);
      end_unwind_protect
    catch
      x = lasterror ();
      fprintf (stderr, "caught error: %s\n", x.message);
    end_try_catch
    fdisp (stderr, "intermediate");
  unwind_protect_cleanup
    fdisp (stderr, "cleanup outer");
  end_unwind_protect
  fdisp (stderr, "outer normal");

then I see the same behavior with your code as I did with the older
unwind protect implementation, and it seems like the correct thing to
do.

jwe


reply via email to

[Prev in Thread] Current Thread [Next in Thread]