develooper Front page | perl.perl5.porters | Postings from September 2013

Re: [perl #119555] require should not clobber $! and $^E on success

Thread Previous
From:
Christian Millour
Date:
September 4, 2013 18:07
Subject:
Re: [perl #119555] require should not clobber $! and $^E on success
Message ID:
522776C9.6010608@abtela.com
Fist a big thanks to whoever took the pain to properly resend this 
ticket to p5p (it apparently got lost). For the sake of those following 
this on rt.perl.org, hereafter is a copy of a clarification exchange I 
had with Zefram.

Le 02/09/2013 12:55, Zefram a écrit :
 > Christian Millour wrote:
 >> In line with this effort, and considering the problem outlined above,
 >> require should do the right thing and restore on success the values
 >> of $! and $^E as they were on invocation.
 >
 > This argument can be used to justify saving and restoring $! and $^E
 > around *any* operation.  With Carp there was some historical 
justification
 > that it had attempted to preserve $!, and I was willing to make that
 > work properly.  require has never attempted to preserve $!, and in fact
 > the clearing suggests that it's actively attempting to signal its own
 > status through $!, which restoration would break.
 >
 > -zefram
 >
Damn. I have spent ages trying to try and craft this ticket so to avoid 
this misunderstanding and didn't succeed . Sorry if I was unclear. I am 
not advocating saving and restoring $! and $^E blindly within require, 
but only when require is *successful*.

If the pseudocode in the pod is to be trusted, require signals errors by 
*dying*, and obviously in that case $! and $^E sould *not* be restored, 
so that the calling code may access those and react accordingly (see 
http://www.nntp.perl.org/group/perl.perl5.porters/2012/12/msg196988.html).

I am specifically *not* advocating using "local ($!, $^E);" at the start 
of require, but rather the following pseudocode

sub require {
     my ($filename) = @_;
     my @saveerrs = ($!, $^E);                # *added*
     if (exists $INC{$filename}) {
         return 1 if $INC{$filename};
         die "Compilation failed in require";
     }
     my ($realfilename,$result);
ITER: {
     foreach $prefix (@INC) {
         $realfilename = "$prefix/$filename";
         if (-f $realfilename) {
             $INC{$filename} = $realfilename;
             $result = do $realfilename;
             last ITER;
         }
     }
     die "Can't find $filename in \@INC";
     }
     if ($@) {
         $INC{$filename} = undef;
         die $@;
     } elsif (!$result) {
         delete $INC{$filename};
         die "$filename did not return true value";
     } else {
         ($!, $^E) = @saveerrs;              # *added*
         return $result;
     }
}

which restores $! and $^E on success, and leaves them untouched for 
inspection on failure.


Le 02/09/2013 13:40, Zefram a écrit :
 > Christian Millour wrote:
 >>          I am not advocating saving and restoring $! and $^E blindly
 >> within require, but only when require is *successful*.
 >
 > Thanks for the clarification.  I did miss that detail of your original
 > message (which on rereading I can now see there).  But it's still an
 > awfully broadly applicable argument.  What else should work to preserve
 > $! on success?  Or rather, what should not?
 >
 > -zefram
 >

for those on p5p who wonder what the hell we are talking about, and 
awaiting sync between perlbug and p5p, the original report can be found at
https://rt.perl.org:443/rt3/Ticket/Display.html?id=119555

To answer your question, my main focus with #116118 has been on trying 
to ensure that croak/confess, as an error reporting mechanism, indeed 
report faithfully on the error rather than corrupting it (in the sense 
that $! and $^E are still fully available, especially their numerical 
values, when the error is caught).

What I am really after is the following strategy for programmatic error 
handling (see #116118 for the whole story) :

eval { some_sub_that_might_die_or_croak; 1 } or do {
   my ($evalerr, $e, $se) = ($@, $!, $^E);
   if (ref $evalerr) {
      ... # deal with exception object
   } elsif (I_can_make_sense_of_this_error_string($evalerr)) {
      ... # deal with error string
   } else { # deal with numerical errors
      if ($e == ENOENT) {
         ...
      } elsif {$e == EACCES) {
         ...
      etc.
   }
};

[the underlying issue being that it is often impossible to make sense of 
$evalerr above when you have to support different OS, versions of perl, 
and locales, as the strings handed back by die/croak "oops: $!, $^E" are 
then essentially unpredictable]

Your "local ($!, $^E);" patch on longmess/shortmess went a long way 
towards that goal. #119555 is intended as a complement, to prevent the 
lurking bug currently inherent with the 'lazy loading of Carp' idiom.

One remaining problem is as follows:
Le 31/12/2012 18:27, Jan Dubois a écrit :
 > Even then you still have the problem that any DESTROY method run while
 > unwinding the stack back to the eval() handler may modify $! and $^E
 > again. So these values would also need to be handled similar to $@.

Once this is solved I believe my concerns with error reporting will have 
been handled, so the argument might be less broadly applicable as you think.

As a general rule, I think ops and subs and constructs should not 
clobber (or reset) $! and $^E when successfull. But I also understand 
that such a requirement might be overkill and too costly to be applied 
bluntly. Still, I hope to raise some awareness of the utility of not 
corrupting gratuitously $! and $^E.

In the specific case of require, I believe that fixing it as suggested 
is easier and on the long term better than hunting for all instances of 
the 'lazy loading of Carp' idiom. YMMV.

Christian


Thread Previous


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