#10963: More functorial constructions
-------------------------------------+-------------------------------------
       Reporter:  nthiery            |        Owner:  stumpc5
           Type:  enhancement        |       Status:  needs_work
       Priority:  major              |    Milestone:
      Component:  categories         |   Resolution:
       Keywords:                     |    Merged in:
        Authors:  Nicolas M. ThiƩry  |    Reviewers:  Simon King
Report Upstream:  N/A                |  Work issues:  Reduce startup time
         Branch:                     |  by 5%. Avoid "recursion depth
   Dependencies:  #11224, #8327,     |  exceeded (ignored)".
  #10193, #12895, #14516, #14722,    |       Commit:
  #13589, #14471                     |     Stopgaps:
-------------------------------------+-------------------------------------

Comment (by SimonKing):

 `WeakValueDictionary` uses
 {{{
 #!python
         def remove(wr, selfref=ref(self)):
             self = selfref()
             if self is not None:
                 del self.data[wr.key]
 }}}
 and `WeakKeyDictionary uses
 {{{
         def remove(k, selfref=ref(self)):
             self = selfref()
             if self is not None:
                 del self.data[k]
 }}}
 as a call-back.

 And I think I see why `WeakValueDictionary` does not crash. Recall from
 comment:95 that I did (of course with more layers)
 {{{
 M[b]=a
 M[c]=b
 M[d]=c
 }}}
 and the only elements with a strong reference being kept are d and a. When
 deleting a, then successively the items keyed by b, c and d are removed
 from the `WeakValueDictionary`.

 But think for a moment what is happening during the callback, in the line
 {{{
     del self.data[wr.key]
 }}}
 When this is first called, `wr` is a weak reference pointing to a, and
 `wr.key` is b. Hence, when `del self.data[wr.key]` is executed, then there
 is still a ''strong'' reference to b, namely in wr.key. Only when the call
 to `remove()` is finished, `wr` will be released, and in this moment the
 last reference to b is gone. Hence, `del self.data[wr.key]` is called
 again, but this time `wr` points to b and `wr.key` is a strong reference
 to c.

 '''Conclusion:''' During the deletion of the dictionary item (b,a), there
 is a strong reference to the key b. Hence, the deletion of the item (c,b)
 will only be started after deletion of the item (b,a) is completed. Hence,
 no recursion.

 But for a `WeakKeyDictionary` things are different. There,we have
 (of course with more layers)
 {{{
 M[b]=a
 M[c]=b
 M[d]=c
 }}}
 and we only keep a reference to a and d. When we delete d, then the item
 (d,c) will be removed. In the line
 {{{
     del self.data[k]
 }}}
 there is no strong reference to the value of `self.data[k]`. Hence, while
 `self.data[k]` is deleted, it could be that the callback of a weak
 reference pointing to this value is invoked.

 And this analysis gives rise to a solution: In the `TripleDictEraser` and
 `MonoDictEraser`, one should not simply do `del bucket[i:i+3]` or `del
 bucket[i:i+7]`, but one should assign a temporary variable to
 `bucket[i+2]` or `bucket[i+6]` that will keep the value alive until the
 call to the eraser is completed, thus, avoiding the recursion.

 I suggest to open a separate ticket for this issue.

--
Ticket URL: <http://trac.sagemath.org/ticket/10963#comment:98>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to