Consider the following example:
This prints "2 main=ARRAY(0x80f01b4)" (or some such). Note that $a is not being destructed. Apparently, perl loses track of the fact that its reference count is going to zero before it calls the END block, and since the END block kills the process, there is no opportunity for mark-and-sweep.END { kill 'INT', $$ } sub DESTROY { print "@{$_[0]}\n"; exit(14); } my $a = bless [1]; my $b = bless [2, $a];
If we don't worry about propagating the status properly, then we still have problems. Consider the following:
which prints:sub DESTROY { print "@{$_[0]}\n"; exit(14); } my $a = bless [1]; my $b = bless [2, $a]; my $c = bless [3, $b];
It appears that exiting during mark-and-sweep terminates garbage collection.3 main=ARRAY(0x80f00c4) 1
Just dying once doesn't work either:
which prints (under Perl 5.6.1 -- perl5.8.2 is OK):my $cleaning_up; sub DESTROY { print "DESTROY @{$_[0]}\n"; my $id = $_[0][0]; @{$_[0]} = (); print "CLEAN $id\n"; return if $cleaning_up++; exit(14); } my $a = bless [1]; my $b = bless [2, $a]; my $c = bless [3, $b];
The problem here is that the exit at the bottom of the call stack obliterates the stack up through destroying $c.DESTROY 3 main=ARRAY(0x80f00c4) DESTROY 2 main=ARRAY(0x80f01b4) DESTROY 1 CLEAN 1
We can fix these problems like this (brace yourself):
use POSIX; { # Fork off a parent whose role is to convert the exit status. my $pid = fork; defined($pid) or die "Fork failed because $!"; if($pid) { { # TBD: This really ought to be `sub { kill $_[0], $pid }' # instead of "IGNORE", but that does bad things on # ctrl-C, probably because the child gets the signal # twice. local $SIG{INT} = "IGNORE"; waitpid($pid,0); } kill &POSIX::WTERMSIG($?), $$ if &POSIX::WIFSIGNALED($?); my $status = &POSIX::WEXITSTATUS($?); if(0x80 < $status && $status < 0xC0) { kill $status - 0x80, $$; } POSIX::_exit($status); } } my $termsig; my $cleaning_up; our $in_dtor; sub DESTROY { # NOTE: Signals that arrive in this method but outside the eval # are still a problem. my $id = $_[0][0]; local ($@, $?); eval { my $sub_dtor = $in_dtor; local $in_dtor = 1; print "DESTROY @{$_[0]}\n"; my $id = $_[0][0]; @{$_[0]} = (); print "CLEAN $id\n"; }; warn "Failed to deallocate $id: $@" if $@; return unless $termsig; return if $in_dtor || $cleaning_up++; exit(14); } eval { local $SIG{INT} = sub { $termsig = $_[0]; die "SIG$_[0]\n" }; { my $a = bless [1]; my $b = bless [2, $a]; my $c = bless [3, $b]; } 0; # Must be here to make handler go out of scope last! }; if($@) { die $@ unless $@ =~ /^SIG(\w+)$/; } END { if($termsig) { use Config; my @sigs = split(' ', $Config{sig_name}); my $i=0; $i++ while $sigs[$i] ne $termsig; $? = 0x80 + $i; } }
In reply to Re^2: Propagating a Signal from DESTROY
by topnerd
in thread Propagating a Signal from DESTROY
by topnerd
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |