DESTROY gotcha

  • Thread starter Rainer Weikusat
  • Start date
R

Rainer Weikusat

As far as I could determine, this isn't documented anywhere, hence, I
thought I should post it: Perl exceptions, including automatically
generated ones caused by attempts to call a non-existant subroutine or
invoke a method using undef as invocant, are not propagated out of
Perl object destructors, ie, the following code

-------------
package Boo;

sub new
{
return bless([], $_[0]);
}

sub DESTROY
{
$_[2]->the_end_of_the_world_lost();
}

package main;

my $o = Boo->new();
$o = undef;

print("So we had to continue ...\n");
------------

will print 'So we had to continue ...' (this caused me quite some head
scratching yesterday because 'some code' on an appliance in Korea
apparently managed to 'escape' from a certain scope without executing
all of the contained code).
 
C

C.DeRykus

As far as I could determine, this isn't documented anywhere, hence, I

thought I should post it: Perl exceptions, including automatically

generated ones caused by attempts to call a non-existant subroutine or

invoke a method using undef as invocant, are not propagated out of

Perl object destructors, ie, the following code



-------------

package Boo;



sub new

{

return bless([], $_[0]);

}



sub DESTROY

{

$_[2]->the_end_of_the_world_lost();

}



package main;



my $o = Boo->new();

$o = undef;



print("So we had to continue ...\n");

------------



will print 'So we had to continue ...' (this caused me quite some head

scratching yesterday because 'some code' on an appliance in Korea

apparently managed to 'escape' from a certain scope without executing

all of the contained code).

Perl special cases exceptions in DESTROY and so they're
untrappable. With -w though you'll at least see:

(in cleanup) Can't call method "the_end_of_the_world_lost"
on an undefined value at ...

Here's some background on the problem (and controversy):
http://www.perlmonks.org/index.pl?node_id=924488
 
R

Rainer Weikusat

C.DeRykus said:
As far as I could determine, this isn't documented anywhere, hence,
[...]
-------------

package Boo;



sub new

{

return bless([], $_[0]);

}



sub DESTROY

{

$_[2]->the_end_of_the_world_lost();

}



package main;



my $o = Boo->new();

$o = undef;



print("So we had to continue ...\n");
[...]

Perl special cases exceptions in DESTROY and so they're
untrappable. With -w though you'll at least see:

(in cleanup) Can't call method "the_end_of_the_world_lost"
on an undefined value at ...

Here's some background on the problem (and controversy):
http://www.perlmonks.org/index.pl?node_id=924488

That's actually about a somewhat different issue, namely, 'user
exceptions' in destructors, not about fatal execution errors perl
can only detect at runtime (and the behaviour I observed occurred on
5.10.1). And the main issue here is really that the 'in cleanup'
explanation is the only documentation about this I'm presently aware
of[*].

[*] Considering that this isn't documented, it is - by definition - a
bug :->.
 
C

C.DeRykus

C.DeRykus said:
As far as I could determine, this isn't documented anywhere, hence,
[...]


-------------

package Boo;



sub new

{

return bless([], $_[0]);

}



sub DESTROY

{

$_[2]->the_end_of_the_world_lost();

}



package main;



my $o = Boo->new();

$o = undef;



print("So we had to continue ...\n");


[...]



Perl special cases exceptions in DESTROY and so they're
untrappable. With -w though you'll at least see:

(in cleanup) Can't call method "the_end_of_the_world_lost"
on an undefined value at ...

Here's some background on the problem (and controversy):



That's actually about a somewhat different issue, namely, 'user

exceptions' in destructors, not about fatal execution errors perl

can only detect at runtime (and the behaviour I observed occurred on

5.10.1). And the main issue here is really that the 'in cleanup'

Hm, the discussion didn't touch on it but perl's
handling of any destructor fatality is the same
eg,

(in cleanup) Illegal division by zero ...

There's at least more explanation in perldiag:

(in cleanup) %s
(W misc) This prefix usually indicates that a
DESTROY() method raised the indicated exception.
...

explanation is the only documentation about this I'm presently aware

of[*].



[*] Considering that this isn't documented, it is - by definition - a

bug :->.

At least a nasty, poorly documented feature...
 
R

Rainer Weikusat

Ben Morrow said:
As of 5.16 this is documented in perlobj,

Not really (except if the behaviour was changed). The corresponding
text is "If your DESTROY method throws an error, this error will be
ignored." But this is not just about application code running from a
destructor invoking die but about any (AFAIK) otherwise fatal runtime
error occurring while running a DESTROY method.

In my case, this was some 'database serialization' code whose
execution was triggered when an object was removed from this database
after the last reference to it went away when the referencing object
was destroyed[*]. This basically uses Data::Dumper to write the
contents of an anonymous array of objects to a text file. Afterwards,
to counteract anything which might have been done when 'freezing' the
objects, Data::Dumper::Freezer) a 'thaw' method is invoked for all
objects in the anonymous array. Because of an error in some other
code, one of the array elements wasn't an object but undef. The "Can't
call ... on an undefined value" error caused by that was ignored. Some
time later, a new object was added to this database, triggering
another serialization, this time not from 'a cleanup context'. Hence,
the attempt to invoke ->thaw using the undef value introduced earlier
aborted now (this was a part of the events which occured prior to this
program going into a crash - restart - crash - restart loop I had to
infer from the available diagnostic output after the 'visible problem'
was noticed and the corrupted on-disk database removed).

[*] Yes, I'm really doing 'application reference counting' for objects
on top of the perl garbage collection mechanism.
 
C

C.DeRykus

Not really (except if the behaviour was changed). The corresponding

text is "If your DESTROY method throws an error, this error will be

ignored." But this is not just about application code running from a

destructor invoking die but about any (AFAIK) otherwise fatal runtime

error occurring while running a DESTROY method.

Hm, maybe eval and exit on error as a safeguard..?

sub DESTROY
{
return if ${^GLOBAL_PHASE} eq 'DESTRUCT';

eval {
...
$_[2]->the_end_of_the_world_lost();
...

};
print "fatal error in DESTROY: $@" and exit 1 if $@;
}

....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top