Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can using Scalar::Util's weaken cause invalid reference problems?

Please see this related question for some background information.

When I say "invalid reference" I mean a reference that points to no data.


Assume we have the following data structure containing cyclic references:

       +-----------------------------------------------------+
       |                                                     |
       +-->+============+    +==========+                    |
           [ Reference ----->[ Blessed  ]                    |
$parent -->+============+    [ Hash     ]                    |
                             [          ]   +==========+     |
                             [ children --->[ Array    ]     |
                             [          ]   [          ]     |
                             +==========+   [ 0: ---------+  |
                                            [          ]  |  |
                                            +==========+  |  |
                                                          |  |
       +--------------------------------------------------+  |
       |                                                     |
       +-->+============+    +==========+                    |
           [ Reference ----->[ Blessed  ]                    |
$child --->+============+    [ Hash     ]                    |
                             [          ]                    |
                             [ parent: ----------------------+
                             [          ]
                             +==========+

I understand that I can use Scalar::Util's weaken function to "weaken" references . . . but what happens if I weaken the reference from parent->child and also weaken the reference from child->parent and then either $child or $parent goes out of scope, but not the other?

Example: $parent goes out of scope so the reference is gone.

       +-----------------------------------------------------+
       |                                                     |
       +-->+============+    +==========+                    |
           [ Reference ----->[ Blessed  ]                    |
           +============+    [ Hash     ]                    |
                             [          ]   +==========+     |
                             [ children --->[ Array    ]     |
                             [          ]   [          ]     |
                             +==========+   [ 0: ---------+  |
                                            [          ]  |  |
                                            +==========+  |  |
                                                          |  |
                 would this break the link? ------------> X  X
                                                          |  |
       +--------------------------------------------------+  |
       |                                                     |
       +-->+============+    +==========+                    |
           [ Reference ----->[ Blessed  ]                    |
$child --->+============+    [ Hash     ]                    |
                             [          ]                    |
                             [ parent: ----------------------+ <--- would this parent object pointer now be invalid?
                             [          ]
                             +==========+

If I did this, and then the "parent" went out of scope, would the parent object be removed from memory because Perl's internal reference count for that object goes to 0? I ask this, because if $child still exists and needs to use some data from the parent object this would cause problems because the child object would now hold an invalid pointer to the parent.

like image 753
tjwrona1992 Avatar asked Oct 25 '25 02:10

tjwrona1992


1 Answers

I'll use the simpler structure created by the following code to answer your questions:

my $x = { };
my $y = $x;
weaken($y);

The following illustrates what this does step by step.

  1. my $x = { };

              +============+      +==========+
    $x -----> [ Reference ------->[ Hash     ]
              [ REFCNT=1   ]      [ REFCNT=1 ]
              +============+      [          ]
                                  +==========+
    
  2. my $y = $x;

              +============+      +==========+
    $x -----> [ Reference ------->[ Hash     ]
              [ REFCNT=1   ]  +-->[ REFCNT=2 ]
              +============+  |   [          ]
                              |   +==========+
              +============+  |
    $y -----> [ Reference ----+
              [ REFCNT=1   ]
              +============+
    
  3. weaken($y);

              +============+      +==========+
    $x -----> [ Reference ------->[ Hash     ]
              [ REFCNT=1   ]  +-->[ REFCNT=1 ]
              +============+  |   [ BACKREFS ---+
                              |   +==========+  |
              +============+  |                 |
    $y -----> [ Weak Ref -----+                 |
         +--> [ REFCNT=1   ]                    |
         |    +============+                    |
         +--------------------------------------+
    

    In addition to setting the WEAKREF flag in the reference, the referenced variable's reference count was lowered, and a backreference was created.

Scenario 1

If $y goes out of scope or is set to a different value, the second reference's REFCNT will drop to zero, which will free the reference. This would normally drop the hash's reference count, except the freed reference was a weak reference. So it will simply remove itself from the list of backreferences instead.

          +============+      +==========+
$x -----> [ Reference ------->[ Hash     ]
          [ REFCNT=1   ]      [ REFCNT=1 ]
          +============+      [          ]
                              +==========+

Scenario 2

If $x goes out of scope or is set to a different value, the first reference's REFCNT will drop to zero, which will free the reference, which will drop the reference count of the hash to zero, which will cause the hash to be freed. As part of that, each backreferenced variables will be made undef.

          +============+
$y -----> [ Undefined  ]
          [ REFCNT=1   ]
          +============+

At this point print("$y->{foo}\n"); will croak (exit with an error message, not a segmentation violation), which you can avoid by checking if $y is defined first.

like image 84
ikegami Avatar answered Oct 26 '25 16:10

ikegami