From: Peter Rabbitson Date: Thu, 4 Jul 2013 05:06:06 +0000 (+0200) Subject: We already explicitly save the value of $@ - local()ization is not needed X-Git-Tag: Try-Tiny-0.13~6^2~7 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=2b0d579d80e5316f00183930df181ea36f80e517;p=p5sagit%2FTry-Tiny.git We already explicitly save the value of $@ - local()ization is not needed 511c05cae introduced an extension to try{} - unlike in an eval{} a try{} block has access to the previous value of $@. Since we already have a copy, we can do a manual restore of $@ instead of the expensive local() mechanism. Results in a 7% speedup of bare try{} on 5.16 --- diff --git a/Changes b/Changes index f9edd71..440be84 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,6 @@ - fix tests failing on 5.6.x due to differing DESTROY semantics + - excise superfluous local($@) call - 7% speedup 0.12 - doc fixes diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index 02f4be1..5923545 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -46,3 +46,5 @@ \.gitignore$ MYMETA + +maint diff --git a/lib/Try/Tiny.pm b/lib/Try/Tiny.pm index db18b3e..9d46a76 100644 --- a/lib/Try/Tiny.pm +++ b/lib/Try/Tiny.pm @@ -41,41 +41,35 @@ sub try (&;@) { } } - # save the value of $@ so we can set $@ back to it in the beginning of the eval - my $prev_error = $@; - - my ( @ret, $error, $failed ); - # FIXME consider using local $SIG{__DIE__} to accumulate all errors. It's # not perfect, but we could provide a list of additional errors for # $catch->(); - { - # localize $@ to prevent clobbering of previous value by a successful - # eval. - local $@; + # save the value of $@ so we can set $@ back to it in the beginning of the eval + # and restore $@ after the eval finishes + my $prev_error = $@; - # failed will be true if the eval dies, because 1 will not be returned - # from the eval body - $failed = not eval { - $@ = $prev_error; - - # evaluate the try block in the correct context - if ( $wantarray ) { - @ret = $try->(); - } elsif ( defined $wantarray ) { - $ret[0] = $try->(); - } else { - $try->(); - }; - - return 1; # properly set $fail to false + my ( @ret, $error ); + + # failed will be true if the eval dies, because 1 will not be returned + # from the eval body + my $failed = not eval { + $@ = $prev_error; + + # evaluate the try block in the correct context + if ( $wantarray ) { + @ret = $try->(); + } elsif ( defined $wantarray ) { + $ret[0] = $try->(); + } else { + $try->(); }; - # copy $@ to $error; when we leave this scope, local $@ will revert $@ - # back to its previous value - $error = $@; - } + return 1; # properly set $fail to false + } and $error = $@; + + # reset the original value of $@ + $@ = $prev_error; # set up a scope guard to invoke the finally block at the end my @guards = @@ -146,7 +140,7 @@ __END__ =head1 NAME -Try::Tiny - minimal try/catch with proper localization of $@ +Try::Tiny - minimal try/catch with proper preservation of $@ =head1 SYNOPSIS @@ -329,8 +323,9 @@ More specifically, C<$@> is clobbered at the beginning of the C, which also makes it impossible to capture the previous error before you die (for instance when making exception objects with error stacks). -For this reason C will actually set C<$@> to its previous value (before -the localization) in the beginning of the C block. +For this reason C will actually set C<$@> to its previous value (the one +available before entering the C block) in the beginning of the C +block. =head2 Localizing $@ silently masks errors diff --git a/maint/bench.pl b/maint/bench.pl new file mode 100644 index 0000000..215097c --- /dev/null +++ b/maint/bench.pl @@ -0,0 +1,14 @@ +#!/usr/bin/env perl + +use warnings; +use strict; + +use Benchmark::Dumb ':all'; +use Try::Tiny; + +my $max = 10_000; + +cmpthese('0.003', { + eval => sub { do { local $@; eval { die 'foo' } } for (1..$max) }, + try => sub { do { try { die 'foo' } } for (1..$max) }, +});