On Tue, Feb 09, 2010 at 02:44:57PM +0000, Nicholas Clark wrote:
> On Tue, Feb 09, 2010 at 02:41:44PM +0000, Tim Bunce wrote:
> > On Mon, Feb 08, 2010 at 04:38:48PM +0000, Tim Bunce wrote:
> > > In the context of PostgreSQL PL/Perl, which uses Safe, David Wheeler
> > > pointed me to a bug in Safe 2.21. The effect is that an exception thrown
> > > from a closure gets lost.
> > >
> > > I've boiled it down to this:
> > >
> > > perl -MSafe -e 'Safe->new->reval(q{sub { die @_ }})->(qq{ok\n})'
> > >
> > > That should die with "ok" but doesn't on 5.10.1 or 5.11.4 or 5.8.6.
> > > It does die, correctly, on a whole bunch of other versions I have
> > > lying around locally.
> > >
> > > This is a heads up as I'm starting to dig. Any thoughts most welcome...
> >
> > Turns out to be a threads issue. Fails with USE_ITHREADS, passes otherwise.
> > (I had a mix of perl lying around with various configs.)
> >
> > Still digging...
>
> git bisect is probably your friend.
>
> There's a section describing how to do it in perlrepository.pod:
>
> http://perl5.git.perl.org/perl.git/blob/0972ecdf:/pod/perlrepository.pod#l626
Thanks. But I din't think a bisect will be helpful here. Safe 2.20+
fails for all perl versions (with threads enabled) that I've tried it on.
The problem is that the closure that wraps any returned code ref if
threads are enabled is acting as an eval block so hiding the exception.
This mostly fixes it:
--- a/Safe.pm
+++ b/Safe.pm
@@ -311,7 +311,15 @@ sub reval {
$ret = sub {
my @args = @_; # lexical to close over
my $sub_with_args = sub { $sub->(@args) };
- return Opcode::_safe_call_sv($root, $obj->{Mask}, $sub_with_args)
+ undef $@; # needed due to perl_call_sv(sv, G_EVAL|G_KEEPERR)
+ my @subret = (wantarray)
+ ? Opcode::_safe_call_sv($root, $obj->{Mask}, $sub_with_args)
+ : scalar Opcode::_safe_call_sv($root, $obj->{Mask}, $sub_with_args);
+ if ($@) { # rethrow exception after removing prefix added by G_KEEPERR
+ $@ =~ s/\t\(in cleanup\) //;
+ die $@;
+ }
+ return (wantarray) ? @subret : $subret[0];
};
}
}
One remaining problem is that G_KEEPERR 'helpfully' generates a warning.
Per perlcall docs:
In addition, a warning is generated using the appended string.
This can be disabled using "no warnings 'misc'".
I tried adding no warnings 'misc' in various places by I couldn't
silence it. Any thoughts?
Tim.