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

烂漫一生 提交于 2019-12-10 18:26:01

问题


Please see this related question for some background information.

Note: 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.


回答1:


  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, 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, 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.




回答2:


It wouldn't be an "invalid reference", it would be undef. When the last non-weak reference to something goes out of scope (or is weakened), then all weak references to that something become undef.

But yes, if the parent has only weak references to its children, and the children have only a weak reference to their parent, then if the only strong reference $parent goes out of scope, any children you still have other references to will now have undef in their parent field.



来源:https://stackoverflow.com/questions/32013133/can-using-scalarutils-weaken-cause-invalid-reference-problems

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!