How can I detect recursive package calls in Perl?

I have a Perl project, I just had a problem making a circular call to the package. The code below demonstrates the problem.

When this is done, each packet will call another, until all the memory of the computer is destroyed and it is blocked. I agree that this is a bad design, and circular challenges like this should not be implemented in the design, but my project is large enough and I would like to detect it at runtime.

I read about the weakened function and Data :: Structure :: Util, but I did not find a way to determine if there is a circular load of the package (I assume because a new copy is made at each iteration and stored in each copy of $ this hash). Any ideas?

use system::one; my $one = new system::one(); package system::one; use strict; use system::two; sub new { my ($class) = @_; my $this = {}; bless($this,$class); # attributes $this->{two} = new system::two(); return $this; } package system::two; use strict; use system::one; sub new { my ($class) = @_; my $this = {}; bless($this,$class); # attributes $this->{one} = new system::one(); return $this; } 
+6
object callstack perl recursion packages
source share
5 answers

There is a code here too. :)

 sub break_recursion(;$) { my $allowed = @_ ? shift : 1; my @caller = caller(1); my $call = $caller[3]; my $count = 1; for(my $ix = 2; @caller = caller($ix); $ix++) { croak "found $count levels of recursion into $call" if $caller[3] eq $call && ++$count > $allowed; } } sub check_recursion(;$) { my $allowed = @_ ? shift : 1; my @caller = caller(1); my $call = $caller[3]; my $count = 1; for(my $ix = 2; @caller = caller($ix); $ix++) { return 1 if $caller[3] eq $call && ++$count > $allowed; } return 0; } 

They are called as:

 break_recursion(); # to die on any recursion break_recursion(5); # to allow up to 5 levels of recursion my $recursing = check_recursion(); # to check for any recursion my $recursing = check_recursion(10); # to check to see if we have more than 10 levels of recursion. 

Perhaps this is CPAN, I think. If anyone has any thoughts on this, please share.

+4
source share

The fact that they are in separate packages has nothing to do with the fact that this is done endlessly, consuming all available resources. You call two methods from within each other. This is not a circular reference; it is a recursion , which is not the same. In particular, weaken won't help you at all. You will get exactly the same effect from:

 sub a { b(); } sub b { a(); } a(); 

The best way to avoid this is to not do it . It is more useful if you have to write recursive functions, try not to use several functions in the recursion chain, but just one, so it’s easier for you to keep track of the time when your calls should end.

As for how to determine if something similar is happening, you need to do something simple, for example, increment a variable with recursion depth and end (or return) if your depth exceeds a certain value. But you really should not rely on this, it is like writing a while and using the increment there to make sure your function is not exhausted. Just don't relearn the kit unless you know how and when it ends.

Another pressing issue is what are you trying to accomplish first?

+4
source share

I suggest making a routine called break_constructor_recursion () that uses caller () to check the call stack as follows:

Find out which method is that the package just called me.

Look at the rest of the call stack if the same method in the same package is somewhere else.

If so, die () with something appropriate.

Then you add the break_constructor_recursion () call to your constructors. If the constructor is called from the inside, it will bomb.

Now it can cause false positives; it is not impossible for the constructor to be legally called inside itself. If you have problems with this, I would say that it just looks for some N additional occurrences of the constructor before it identifies the error. If the system has 20 calls to system :: two :: new (), the likelihood that you are not recursive is pretty low.

+3
source share

The classic double recursion break is to use a state variable to determine if you are already inside a function:

 { my $in_a; sub a { return if $in_a; #do nothing if b(), or someone b() calls, calls a() $in_a = 1; b(); $in_a = 0; } } 

You can do whatever you want if $in_a true, but die ing or refund is common. If you are using Perl 5.10 or later, you can use the state function instead of inserting the function into your scope:

 sub a { state $in_a; return if $in_a; #do nothing if b(), or someone b() calls, calls a() $in_a = 1; b(); $in_a = 0; } 
+2
source share

use warnings;

without warning:

 #!/usr/bin/perl use strict; sub foo { foo(); } foo(); 

-

  $ perl script.pl 
 ^ C # after death 

with warnings:

 #!/usr/bin/perl use strict; use warnings; sub foo { foo(); } foo(); 

-

  $ perl script.pl 
 Deep recursion on subroutine "main :: foo" at script.pl line 7.
 ^ C # after death 

Always use warnings.

use warnings FATAL => qw( recursion );

 #!/usr/bin/perl use strict; use warnings FATAL => qw( recursion ); sub foo { foo(); } foo(); 

-

  $ perl script.pl 
 Deep recursion on subroutine "main :: foo" at script.pl line 7.
 $ 
+1
source share

All Articles