A Subtle Untying Trap

If you intend to make use of the object returned from tie or tied, and the class defines a destructor, there is a subtle trap you must guard against. Consider this (admittedly contrived) example of a class that uses a file to log all values assigned to a scalar:

package Remember;sub TIESCALAR {
    my $class = shift;
    my $filename = shift;
    open(my $handle, ">", $filename)
         or die "Cannot open $filename: $!
";
    print $handle "The Start
";
    bless {FH => $handle, VALUE => 0}, $class;
}
sub FETCH {
    my $self = shift;
    return $self->{VALUE};
}
sub STORE {
    my $self = shift;
    my $value = shift;
    my $handle = $self->{FH};
    print $handle "$value
";
    $self->{VALUE} = $value;
}
sub DESTROY {
    my $self = shift;
    my $handle = $self->{FH};
    print $handle "The End
";
    close $handle;
}
1;

Here is an example that makes use of our Remember class:

use strict;
use Remember;

my $fred;
$x = tie $fred, "Remember", "camel.log";
$fred = 1;
$fred = 4;
$fred = 5;
untie $fred;
system "cat camel.log";

This is the output when it is executed:

The Start
1
4
5
The End

So far, so good. Let's add an extra method to the Remember class that allows comments in the file--say, something like this:

sub comment {
    my $self = shift;
    my $message = shift;
    print { $self->{FH} } $handle $message, "
";
}

And here is the previous example, modified to use the comment method:

use strict;
use Remember;

my ($fred, $x);
$x = tie $fred, "Remember", "camel.log";
$fred = 1;
$fred = 4;
comment $x "changing…";
$fred = 5;
untie $fred;
system "cat camel.log";

Now the file will be empty, which probably wasn't what you intended. Here's why. Tying a variable associates it with the object returned by the constructor. This object normally has only one reference: the one hidden behind the tied variable itself. Calling "untie" breaks the association and eliminates that reference. Since there are no remaining references to the object, the DESTROY method is triggered.

However, in the example above we stored a second reference to the object tied to $x. That means that after the untie there will still be a valid reference to the object. DESTROY won't get triggered, and the file won't get flushed and closed. That's why there was no output: the filehandle's buffer was still in memory. It won't hit the disk until the program exits.

To detect this, you could use the -w command-line flag, or include the use warnings "untie" pragma in the current lexical scope. Either technique would identify a call to untie while there were still references to the tied object remaining. If so, Perl prints this warning:

untie attempted while 1 inner references still exist

To get the program to work properly and silence the warning, eliminate any extra references to the tied object before calling untie. You can do that explicitly:

undef $x;
untie $fred;

Often though you can solve the problem simply by making sure your variables go out of scope at the appropriate time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset