
On Wed, 20 Jan 2016 at 15:46 Victor Stinner <victor.stinner@gmail.com> wrote:
Hi,
2016-01-20 22:18 GMT+01:00 Glenn Linderman <v+python@g.nevcal.com>:
On 1/20/2016 12:50 PM, Brett Cannon wrote:
A global (shared between all dicts) unit64 ma_version is actually quite reliable -- if a program does 1,000,000 dict modifications per second, it would take it 600,000 years till wrap-around.
I think that Yury found a bug in FAT Python. I didn't test the case when the builtins dictionary is replaced after the definition of the function. To be more concrete: when a function is executed in a different namespace using exec(code, namespace). That's why I like the PEP process, it helps to find all issues before going too far :-)
I like the idea of global counter for dictionary versions. It means that the dictionary constructor increases this counter instead of always starting to 0.
FYI a fat.GuardDict keeps a strong reference to the dictionary. For some guards, I hesitated to store the object identifier and/or using a weak reference. An object identifier is not reliable because the object can be destroyed and a new object, completly different, or of the same type, can get the same identifier.
But would invalidate everything, instead of just a fraction of things, on every update to anything that is monitored...
I don't understand this point.
I think Glenn was assuming we had a single, global version # that all dicts shared *without* having a per-dict version ID. The key thing here is that we have a global counter that tracks the number of mutations for *all* dictionaries but whose value we store as a *per-dictionary* value. That ends up making the version ID inherently both a token representing the state of any dict but also the uniqueness of the dict since no two dictionaries will ever have the same version ID.
In short, the guard only has to compare two 64 bit integers in the fast-path, when nothing changed. For a namespace, it means that no value was replaced in this namespace.
If a different namespace is modified, the version of the watched namespace does not change, so we are still in the fast-path.
If a value is replaced in the watched namespace, but not the watched variable, we have to take a slow-path, hopefully only once.
The worst case is when a value different than the watched value is modified between each guard check. In this case, we always need a dict lookup. An heuristic can be chosen to decide to give up after N tries. Currently, fat.GuardDict always retries.
Does "retries" mean "check if the value really changed, and if it hasn't then just update the version ID the guard checks"?