NAME

Data::Decycle - (Cyclic|Circular) reference decycler

VERSION

$Id: Decycle.pm,v 0.2 2010/08/23 09:11:03 dankogai Exp dankogai $

SYNOPSIS

use Data::Decycle;

# none of them leak
{
    my $guard = Data::Decycle->new;
    add $guard my $cyclic_sref = \my $dummy;
    $cyclic_sref = \$cyclic_sref;
    add $guard my $cyclic_aref = [];
    $cyclic_aref->[0] = $cyclic_aref;
    add $guard my $cyclic_href = {};
    $cyclic_href->{cyclic} = $cyclic_href;
}

# or register all at once
{
    my $guard = Data::Decycle->new(
      my $cyclic_sref = \my $dummy,
      my $cyclic_aref = [],
      my $cyclic_href = {}
    );
    $cyclic_sref = \$cyclic_sref;
    $cyclic_aref->[0] = $cyclic_aref;
    $cyclic_href->{cyclic} = $cyclic_href;
}

Code Reference and PadWalker

If you have PadWalker, you can decycle closures, too.

{
    my $guard = Data::Decycle->new;
    my $cref;
    $cref = sub{ $_[0] <= 1 ? 1 : $_[0] * $cref->($_[0] - 1) };
    $guard->add($cref);
    print $cref->(10);
}

Functional Interface

You can also cope with circular references explicitly

use Data::Decycle ':all';
my $obj = bless {}, 'Dummy';
$obj->{me} = $obj;
print may_leak($obj);       # true
weaken_deeply($obj);
print may_leak($obj);       # false
print has_cyclic_ref($obj); # true
decycle_deeply($obj);
print $obj->{me} == undef;  # true

as a base class

You can also use it as a base class.

{
  package Dummy;
  use base 'Data::Decycle';
  sub new { bless $_[1], $_[0] }
  sub DESTROY { warn "($_[0])" }
}
{
  my $mom = Dummy->new( {} );
  my $son = Dummy->new( {} );
  say "($mom) has cyclic ref ? ", $mom->has_cyclic_ref ? 'yes' : 'no';
  say "($son) may leak ? ",       $son->may_leak?        'yes' : 'no';
  $mom->{son} = $son;
  $son->{mom} = $mom;
  say "($mom) has cyclic ref ? ", $mom->has_cyclic_ref ? 'yes' : 'no';
  say "($son) may leak ? ",       $son->may_leak?        'yes' : 'no';
  $mom->weaken_deeply;
  $son->weaken_deeply;
  say "($mom) has cyclic ref ? ", $mom->has_cyclic_ref ? 'yes' : 'no';
  say "($son) may leak ? ",       $son->may_leak?        'yes' : 'no';
}

DESCRIPTION

Perl programmers love to hate cyclic References, or circular references. It easly leaks out of perl's reference-counter based garbage collection and stays there until perl exits.

Even with the introduction of weak references in Perl 5.8, you still have to tell perl explicitly to weaken references and which reference to weaken is tricky.

use Devel::Peek;
use Scalar::Util qw/weaken/;
my $obj = {you => $ENV{USER}};
$obj->{me} = $obj;

weaken($obj);      # wrong
weaken($obj->{me}) # right;

In addition to that, weak references do not work with code references.

my $cref;
$cref = sub { $_[0] <= 1 ? 1 : $_[0] * $cref->( $_[0] - 1 ) };
print $cref->(10);
weaken($cref);     # does undef($cref)
print $cref->(10); # goodbye

This module offers something easier than that.

HOW DOES IT WORK?

See the source :-p

Okay, I'll be nicer. Consider the code below again.

{
    my $guard = Data::Decycle->new(
      my $cyclic_sref = \my $dummy,
      my $cyclic_aref = [],
      my $cyclic_href = {}
    );
    $cyclic_sref = \$cyclic_sref;
    $cyclic_aref->[0] = $cyclic_aref;
    $cyclic_href->{cyclic} = $cyclic_href;
}

What happens when it reaches out of the block? $guard will surely be DESTROY()'ed. So it is guaranteed to trigger $guard->DESTROY. And in there it applys decycle_deeply to each reference registered.

Simple, huh?

DEPENDENCY

None except for core modules.

To handle code references correctly, you need to have PadWalker installed.

EXPORT

None by default. Please import explicitly.

METHODS

new
add

see "SYNOPSIS"

SUBROUTINES

may_leak($obj)

checks if $obj may leak. That is, contains a circular reference that is not weak.

has_cyclic_ref($obj)

checks if cyclic reference exists in $obj.

decycle_deeply($obj)

undefs all duplicate references in $obj thus breaks all cyclic reference. Unlike weaken_deeply, it decycles even code references. You shouldn't call it yourself; let decycle take care of it.

weaken_deeply($obj)

weaken all duplicate references in $obj. Unlike decycle_deeply it leaves code references intact. So you can safely call it but you are at your own if $obj contains a code reference that cycles.

recsub { }

Consider the code below:

my $fact;
$fact = sub { $_[0] <= 1 ? 1 : $_[0] * $fact->($_[0]-1) };

This leaks since $fact is now a cyclic reference. with the combination of recsub and $CALLEE, you can rewrite the code as:

my $fact = recsub { $_[0] <= 1 ? 1 : $_[0] * $CALLEE->($_[0]-1) };

To use this feature, you should import both recsub and $CALLEE as:

use Data::Decycle qw(recsub $CALLEE);

or import just recsub and define your own $CALLEE:

use Data::Decycle qw(recsub);
our $CALLEE;

Unlike the previous example, this one dow not leak. See Sub::Recursive for more complicated examples such as mutually recursive subrefs.

AUTHOR

Dan Kogai, <dankogai+cpan at gmail.com>

BUGS

Please report any bugs or feature requests to bug-data-decycle at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Decycle. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Data::Decycle

You can also look for information at:

ACKNOWLEDGEMENTS

PadWalker

You need this if you want to handle code references properly. When you don't have one this module simply does nothing when it encounters them.

Devel::Cycle

Good for inspection -- rather overkill. Decycling features missing.

LICENSE AND COPYRIGHT

Copyright 2010 Dan Kogai.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.