http://www.perlmonks.org?node_id=138636

(scroll to the bottom for the code)

Cafeteria. Enter Chef, boys.

Chef: Hello there, children!

The boys: Hey, Chef.

Chef: How's it goin'?

The boys: Bad.

Chef: Why bad?

Petruchio: My Perl script won't run right.

Chef: Oh, that's too bad. What's wrong with it?

Perl: Mrgmrmph.

Petruchio: I think it's that stupid tie.

Chef: Woah, hold on there, Petruchio! tye's a very good coder, I'm sure he wouldn't...

Petruchio: Dude, not that tye! You know, the tie that lets you disguise your own object as an ordinary Perl variable, so you can use the nice syntax.

Perl: Mremrmrm

Chef: Oh, I see. Well, you know, children, when I'm fixing one of my recipes, I like to reduce it to a minimal test case. I replace as many variables as possible with literals, remove subroutines so the test is short and linear, and so on. I keep taking things away, but I keep the bug there. Then I can usually see what I've done wrong.

Perl: Mwramergmph.

Petruchio: Oh, you mean like:

perl -e 'package X;sub TIEHASH{bless{},shift};tie my %x,"X";sub DESTROY{untie %x}'

Segementation Fault

Arguile: What the...?

Petruchio: Oh my God! Tie killed Perl!

OeufMayo: Bastard!

Chef: My, my. That's not your bug, Petruchio. Perl shouldn't do that, even though your example is pretty strange. But what are you running? I'm using Perl 5.6.0 on DEC/Alpha, and it's not segfaulting for me.

Petruchio: I'm running 5.6.1 on Debian Linux. Hey! it's not segfaulting if I use Perl 5.005_03 on Debian or FreeBSD. But when I SSH to a Redhat box and run it with 5.6.0, it segfaults and dumps core.

OeufMayo: Dude, Win32 5.6.1 segfaults too!

Arguile: Shall I try on 5.6.1 for FreeBSD, and the shame version for OSX? Schweet! A shegfault on FreeBSD, and a bus error on MacOSX. But I'm on that over an SSH connection so it might be shegfault as well.

Chef: But Petruchio, what were you trying to do, anyway? You know the DESTROY sub only gets called after the UNTIE sub, so why are you calling untie there?

Petruchio: Oh, I know, Chef. I was getting strange errors and unexpected exits because I was storing a reference to the variable being tied in a lexical array. You know, a flyweight object. The variable's original value seems to disappear when you tie it, and reappear again when you untie it. It's a lot like local. In fact, I suspect it uses the same mechanism somehow.

Chef: I see. But...

Petruchio: Anyway, I wanted the tied variable to keep the values it had just before it was untied. So I kept a reference to it in the lexical array, and then tried to reset the value during the DESTROY block, once the underlying variable had been uncovered.

Chef: I see. But don't you think...

Petruchio: And really weird stuff happens when you you start tying the different elements in a tied hash or array... and then if you alias typeglobs to them...

Arguile: Dude, your schript sucks.

Petruchio: Shut up, Arguile!

Chef: Well... Petruchio... didn't Mr. Tilly tell you not to go abusing tie like that?

Petruchio: Yeah, I know. They're all really objects anyway, and by pushing tie too far I'm making the code more complex and error-prone. Tie should make things easier. But it was interesting, and now I understand tie better.

Chef: Oh, I understand. I like a little syntactic sugar myself, heh, heh. Expressing love so sweet... baby, you know I want to... taste that sugar...

Petruchio: Chef?

Chef: Oh, uh... yeah, Petruchio?

Petruchio: Here's the important part of my code:

#!/usr/bin/perl package Eraseme; use strict; use Data::Dumper; my @object; tie my %x, 'Eraseme'; print "Outside:\n"; print Dumper( \@object ); #print keys %{ $object[0][0] }; print "Uncommenting this won't prevent + it.\n"; #print Dumper( $object[0][0] ); print "Uncommenting this won't either. +\n"; #untie %x; print "Uncommenting this, however, prevents the error.\n"; sub TIEHASH { push @object, [ \%x ]; my $ret = bless { }, 'Eraseme'; print "Inside:\n"; print Dumper( \@object ); $ret; } sub DESTROY { # print Dumper( $object[0][0] ); print "Uncomment this. See? It exite +d.\n"; # untie %x; print "Uncomment this, and Segfault! +.\n"; print keys %{ $object[0][0] }; print "Destroyed\n"; } sub FIRSTKEY { } sub STORE { } sub FETCH { } sub NEXTKEY { } sub EXISTS { } sub DELETE { } sub CLEAR { }

Chef: Well, isn't that something? Says, (in cleanup) Can't call method "FIRSTKEY" on an undefined value at ./Eraseme.pl line 31 during global destruction.

Petruchio: Yeah! Only the one-liner didn't segfault for you, so it can't be the same bug, can it?

Chef: I don't know, Petruchio. It could still be your bug. you know. Why don't you ask your little PerlMonk friends to look at it? Some of them might have some interesting things to say about it.

Petruchio: Yeah! I'll write it up! Thanks, Chef.

Arguile: Come on. I want some Cheesy Poofs.

Exeunt boys

Chef: My, my, they do get themselves into trouble. Oh my gosh! It's almost noon - I'll be late for the award ceremony! hmm hmm hmm... You know... Kathy Lee... you are a very special woman...

Exit Chef

Update - many thanks to tye for educating me on the proper use of the word 'exeunt'. It means, 'they go out', and is used particularly when speaking in the plural. The boys exeunt, the Chef exits.

Replies are listed 'Best First'.
(tye)Re: Oh my God! Tie killed Perl!
by tye (Sage) on Jan 14, 2002 at 22:36 UTC

    Just a quick note that globals get destroyed in somewhat random order and that can often result in references turning into undef in the middle of "global destruction". Actually, they are still references (because ref returns a true value), they just have an address of 0 (they return 0 in a numeric context), which Perl reports as "undef" when you actually try to use it.

    I wish Perl would at least have an option to, during clean up, decrement the ref count on all globals, destroying things in the normal manner, and after that, would then go sweep up anything that was left over.

    That only partially addresses one of your questions, but that was all I have time for at the moment. (:

    Update: Hmm, you aren't using any globals so this probably isn't the problem, though. Hmm...

    Oh, wait. You have hard-coded references to your lexical %x in your subroutines. This means that %x won't be freed until those subroutines are freed and subroutines don't get freed until "the end". Change your code so that \%x is passed in to TIEHASH (and keep the reference to it commented out in DESTROY) and see how that changes things.

            - tye (but my friends call me "Tye")

      I wish Perl would at least have an option to, during clean up, decrement the ref count on all globals, destroying things in the normal manner, and after that, would then go sweep up anything that was left over.

      I think that this has been fixed, to some extent, in 5.7.2. The following is from the Changes file.

      [ 7991] By: jhi on 2000/12/05 15:52:34 Log: Subject: Re: [PATCH] The largest hoax of all times? From: Ilya Zakharevich <ilya@math.ohio-state.edu> Date: Tue, 5 Dec 2000 00:40:25 -0500 Message-ID: <20001205004025.A4050@monk.mps.ohio-state.edu +> ... Fix the unpredictable order of DESTROYs. ...
      The fix was also discussed in the p5p digest.

      Nevertheless, on 5.7.2 I still have problems with Perl objects that contain sub-objects which are destroyed too early. However, I am not sure if this is part of the same problem.

      --
      John.

        No, that patch fixes a completely different "problem" that never bothered me at all, something that I don't consider a "problem" much less a "bug".

        The patch causes unrelated objects to be destroyed in the reverse of the order in which they were created.

        The problem I'm talking about is that related objects (that is, objects where one object holds a reference to the other) can be destroyed in the wrong order if the objects manage to live until the "global destruction" phase of the Perl interpretter shutdown.

                - tye (but my friends call me "Tye")
Re: Oh my God! Tie killed Perl!
by perrin (Chancellor) on Jan 15, 2002 at 01:01 UTC
    Tied variables are a very kewl feature, but ultimately I think they're usually a mistake. Calling the equivalent methods directly gives better performance. Sometimes tie can be good for debugging or for making changes without modifying legacy code, but I think explicit OO generally ends up being safer and faster.
Re: Oh my God! Tie killed Perl!
by Rich36 (Chaplain) on Jan 14, 2002 at 22:19 UTC
    Informative and entertaining. Kick ass!
    "Ties are bad, m'kay."
    Rich36
    There's more than one way to screw it up...