-
-
Notifications
You must be signed in to change notification settings - Fork 491
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't install callbacks on values of TripleDict, MonoDict #14159
Comments
comment:1
What do you mean by "value under that key"? Ah! You mean this situation: Indeed, this would be bad. Potential solutions (two alternatives):
|
comment:2
Replying to @simon-king-jena:
I guess you could reach into the Just deleting the So probably there needs to be an additional option on And then It feels like horrible feature creep, but I guess you have a good use case (can you come up with an alternative solution?) |
comment:3
Replying to @nbruin:
So, what about the first option? It would make sure that an entry will only be deleted if the value is identical with the object whose callback is being called. |
comment:4
Replying to @simon-king-jena:
Yes, sorry for not being clear about it. That's what the second part of the reply expands on. |
comment:5
Replying to @nbruin:
Sorry, I don't see the relation with suggestion 1.
Why? The callback would be checking that the value is identical with the object pointed to by the weak reference. This should be cheap enough to be done by default. |
comment:6
I thought that the following would expose the problem, but it doesn't:
So, v got collected and thus the callback was executed. However:
The entry that was previously holding v did not get deleted when v's callback was called. So, are we really in trouble? |
comment:7
You have to make sure that the weakref object lives to see the death of v (and ensure that you make such a weakref in the first place!)
I admit, the weakref surviving outside the dict is not a terribly likely event, but from your code at #11521 it wasn't entirely clear to me that you can absolutely rule it out. |
comment:8
Replying to @nbruin:
Apparently I had wrong memories about my code, then. I had in mind that the
Hm. This particular |
comment:9
Replying to @simon-king-jena:
I was fully expecting that to be true, but I could not come up with an explanation for the bug Jeroen reported at #12313, where the line in
leads to a key error in
The only thing I could think of for that line to fail is:
In this scenario, values in this We need to ensure that Hence the formulation of the ticket: If you can prove that installing this callback is safe, I have no problem with it. |
comment:10
Ouch, I looked at that code and you're correct: That doesn't overwrite the value at all (it's really a cache). Incidentally, at that code:
Since
since something is going very wrong if this is not the case! (H would not be allowed to sit there in the dict). Furthermore, I think that only leaves the possibility that On the other hand, since this is a global _cache, it does get to hold quite a few keys, so it's quite likely that it underwent a It's rather difficult to do an accurate postmortem with so little data. |
patch that got misplaced on #12313 |
comment:11
Attachment: trac_12313-revert_callback_from_11521.patch.gz Thank you for moving the patch to the right location (I forgot about the existence of this ticket...). |
Author: Simon King |
Documentation to explain the problem (and possible future problems) |
comment:12
Attachment: trac_14159-document_callback_issue.patch.gz "Reviewer" patch to provide some doc and pointers if this ever proves to be a problem. An example of the kind of situation where this could lead to quadratic memory use (relative to what is required):
This leaves I think we should leave ample documentation of this leak in the code, including the strategy to fix it, should it become necessary. (for posterity: The KeyedRef alternative is probably OK, because it seems that H can never outlive its domain and codomain X and Y, so I don't see how their id's could get reused before the callback on H has happened or got purged. I'm just very cautious with anything that has to do with GC and callback orders after the problems we encountered with it elsewhere. In any case, the incantation would have to documented "DO NOT COPY THIS WITHOUT UNDERSTANDING THE CONSEQUENCES FULLY", because it's a bad pattern to use elsewhere, unless you prove a whole bunch of extra conditions.) Simon, are you OK with the doc? If so we can put this to positive review (doctests passed for me) |
Reviewer: Nils Bruin |
comment:14
Since your patch changes code (by inserting assert statements) I better run the doc tests with #14159, #12313 and #13387, before changing it to positive review. Note, however, that the error reported by the patchbot probably is unrelated:
|
comment:15
A little data point for how this change might adversely affect memory and speed performance. Running
Reference:
With this patch:
This of course is an example that tries to expose exactly the flaw. We're finding:
I'm not sure how serious this is. It's making me a little hesitant on whether we should be "fixing" this at all, though! |
comment:16
Idea: We could add a callback---but a new one. Namely a callback, that will only remove the entry from the |
Attachment: trac_14159_safer_callback.patch.gz A safe callback for weak values of a |
comment:17
I did not run the full tests yet, but I think the new patch can be reviewed. I introduce a variant of The patch contains tests demonstrating that Apply trac_14159_safer_callback.patch |
This comment has been minimized.
This comment has been minimized.
Dependencies: #13387 |
comment:18
PS: I worked on top of #13387, so, that's a dependency. Apply trac_14159_safer_callback.patch |
comment:19
tests OK for me. One tiny nitpick:
There's another one, that the |
Work Issues: Make part the coverage script happy |
comment:43
regular method call protocol is pretty expensive:
(but of course, from cython, one should absolutely use the cdef form) |
comment:44
Some other timings, making set/get cpdef:
Hence, I can confirm that in Python one better relies on the magical methods, while in cython it is better (but not much better!) to use the cpdef methods. But in fact, I found some spots in parent_old.pyx that are used and call the magical methods. |
This comment has been minimized.
This comment has been minimized.
comment:45
I have added a new patch, that will hopefully make Sage a little faster. This is by using the cdef set/get methods everywhere in cython. The only spot that is using the magical methods i sage/categories/homset, which currently is python. Perhaps it should be cythoned? Since homsets are in the background for every conversion and coercion, this could be of some benefit. Apply trac_14159_weak_value_triple_dict.patch trac_14159_use_cdef_get.patch |
comment:46
PS: In any case, cythoning sage.categories.homset should be on a different ticket. |
comment:47
Note that different patchbots show different results for the plugins: One patchbot, running sage-5.8.beta2, does not complain at all. Another one, running sage-5.8.beta1, complains both for startup_modules and startup_time. |
comment:48
Replying to @simon-king-jena:
For the startup module I think that's because 5.8b2 has #13387 merged (for now at least). |
comment:49
#14254 is a blocker that will likely to be merged before this ticket here is positively reviewed. Hence, it will be a dependency. Question: Shall we remove the new function signed_id introduced in #14254? It is not needed with the approach that we take here. Alternatively, we could apply it. How much is the overhead of calling a cpdef inline function doing |
comment:50
Apparently there is no significant difference:
|
comment:51
Replying to @simon-king-jena:
It's If the function is not really in the way, it may be worth keeping around. I have found it immensely useful for debugging |
comment:52
Replying to @nbruin:
+5 So, let's keep it (it is cpdef anyway, hence, can be used from Python as well), and apparently it is fast enough (in the example above I had "cdef inline", but "cpdef inline" gives the same timings). |
comment:53
The first patch needed to be rebased, the second was still fine. Apply trac_14159_weak_value_triple_dict.patch trac_14159_use_cdef_get.patch |
Optional weak values for mono- and tripledict |
comment:54
Attachment: trac_14159_weak_value_triple_dict.patch.gz I needed another update of the patch: #14254 has introduced signed_id, which is imported in homset.py, but with the approach from here, we do not need to import signed_id. Hence, I removed the import statement in the current version of the patch. Apply trac_14159_weak_value_triple_dict.patch trac_14159_use_cdef_get.patch |
comment:55
Good stuff! Much cleaner. Patchbot is happy and effectively this patch consolidates some use of |
comment:56
Replying to @nbruin:
That's not entirely true, at least not with GCC. The |
comment:58
attachment: trac_14159_use_cdef_get.patch needs a proper commit message |
Use cdef get/set methods of |
comment:59
Attachment: trac_14159_use_cdef_get.patch.gz Message added. |
Merged: sage-5.10.beta0 |
In #11521 trac_11521_callback.patch a callback was installed on a weakref value of a TripleDict:
That's not safe: If the value under that key gets changed while the value remains in memory, the callback will be executed and remove an entry that now has an unrelated value!
So: Either prove that the value under this key will not change for the lifetime of H (keep in mind that cyclic references can extend the lifetime of an otherwise unreachable object essentially indefinitely, so the proof needs to include that all key components survive H, otherwise those ids could get reused) or provide a more selective callback (for instance, ensure that the value is still as expected before deleting).
Note: The patch trac_14159_safer_callback.patch follows the second approach, so that a memory leak remains fixed.
Another point is that the API of
_cache.eraser
isn't really published, so this behaviour is probably better encapsulated in a method on the dict itself.See #12313 comment 317 for a situation that likely implicates these callbacks (someone has to hold strong references to these keys in order to set the dict, so the absence of the keys suggests a spurious execution of this kind of callback)
Apply
Depends on #13387
Depends on #14254
CC: @simon-king-jena @jpflori
Component: memleak
Author: Simon King
Reviewer: Nils Bruin
Merged: sage-5.10.beta0
Issue created by migration from https://trac.sagemath.org/ticket/14159
The text was updated successfully, but these errors were encountered: