develooper Front page | perl.perl5.porters | Postings from May 2010

Re: more reliable exception handling

Thread Previous | Thread Next
From:
Zefram
Date:
May 4, 2010 13:30
Subject:
Re: more reliable exception handling
Message ID:
20100504202946.GA23459@lake.fysh.org
Andy asked about the jobs of $@, which I eliptically referred to.
Before my patches, $@/ERRSV had these jobs:

    a. convey the exception (or lack thereof) caught by an eval to the
       immediately following code

    b. input exception being rethrown or warned about to Perl-level
       die()/warn()

    c. store an exception being thrown during the stack-unwinding phase
       of throwing

    d. input non-string exception to C-level croak() et al

    e. accumulate multiple exceptions caught at one level (allegedly,
       in code that uses G_KEEPERR, but I've never seen real code use
       it this way)

It might be argued that some group of these jobs actually constitutes a
single job, where the things I've listed are linked by just leaving an
exception in $@.  But I reckon these are all logically distinct purposes.
Crucially, some of these jobs interfere with each other, most prominently
that job c conflicts with all of the jobs, including itself.

Jobs a and b are the only properly documented purposes of $@ (in
L<perlvar> and L<perlfunc>), and of course I've not changed them.
I've completely relieved $@ of job c, which is now taken over by a mortal
SV referenced only from a C variable in die_unwind().  I've made job d
achievable by other means (croak_sv() et al), and moved the core uses over
to the new means, so $@ is mostly relieved of that job, but the option
to use $@ that way remains in the C-level API for backward compatibility.

Job e got broken, in that $@ no longer implicitly accumulates exceptions
in that situation: instead it'll just keep its initial value, either blank
or a primary exception.  It's a funny usage, and I'm not convinced that
anything actually intentionally uses that behaviour.  We've got better
approaches to multiple exceptions now, in the new structured world.

Jesse asked about the Perl-visible changes resulting from my patches.
In the following list I'm trying to be complete, at the expense of having
some of the list entries overlap.  Single implementation changes tend
to have multiple results around here, and there are multiple ways to
encounter some of the effects.

    * Where an exception is thrown, code running early in the throwing
      process (e.g., in &{$SIG{__DIE__}}) will now see a consistent
      behaviour for the contents of $@, where previously the behaviour
      differed depending on whether the exception was a string or not.
      Previously, a non-string exception would appear in $@, but a string
      exception would leave the former value of $@ unchanged.  Now $@
      will be unchanged at this point in either case.  (Note that the
      exception being thrown is properly presented to &{$SIG{__DIE__}}
      as a parameter, and this part is *not* changing.)

    * Where an exception is thrown, and code running early in the
      throwing process (e.g., in &{$SIG{__DIE__}}) writes to $@ (by
      assignment or internal eval), this will not affect the exception
      being thrown, where previously it sometimes would.  To be precise,
      previously, a write to $@ at this point would replace the exception
      being thrown, iff the exception originally being thrown was
      a non-string.  (This opportunity to clobber occurs in exactly
      the same circumstances as the visibility of the exception in $@
      discussed in the previous item.)

    * Where an exception is thrown, code running during unwinding (e.g.,
      destructors) will see $@ containing its former contents, where
      previously it would see the exception being thrown.

    * Where an exception is thrown, and code running during unwinding
      (e.g., destructors) writes to $@ (by assignment or internal eval),
      this will not affect the exception being thrown, where previously
      it would replace the exception being thrown.

    * Where $@ is local()ised inside an eval, this will not affect
      exception throwing for that eval, where previously the restoration
      of $@ would clobber any exception being thrown.  This is a special
      case of the previous item.

    * Where Perl-level warn() is called with a single non-string argument
      (i.e., a structured warning object), &{$SIG{__WARN__}} will be
      passed the object itself, where previously it was passed the result
      of stringifying the object.

    * Where Perl-level warn() is called with no arguments, thus picking
      up the thing to warn about from $@, and $@ holds a non-string
      exception, the exception will be used as-is, where previously it
      was stringified and had text appended to it.

    * Where code is called with G_KEEPERR (e.g., a DESTROY method) and
      throws a string exception, and the ambient value of $@ is a string,
      the ambient $@ will remain unchanged, where previously the inner
      exception was appended to it.

    * Where code is called with G_KEEPERR (e.g., a DESTROY method) and
      throws an exception, and the ambient value of $@ is not a string,
      the ambient $@ will remain unchanged, where previously the inner
      exception replaced it.

    * Where code is called with G_KEEPERR (e.g., a DESTROY method)
      and throws a string exception, and a string-identical exception
      was recently thrown by G_KEEPERRed code called by the same outer
      code, the repeat exception will result in a repeat warning, where
      previously the warning was suppressed.

    * Where code is called with G_KEEPERR (e.g., a DESTROY method) and
      throws a non-string exception, the ambient $@ will remain unchanged,
      where previously the inner exception replaced it.

    * Where code is called with G_KEEPERR (e.g., a DESTROY method)
      and throws a non-string exception, the exception will be emitted
      as a warning (just like a string exception), where previously no
      warning was generated.

Looking back at the list, this is potentially a monster perldelta entry.
But it reduces the complexity of the exception behaviour, as you can in
part see by the shortening of the L<perlcall> segment.

-zefram

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About