-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
WeakKeyDict based on object ids #3002
Comments
|
Yes. And I think that is what my concern is about, in WeakKeyDict. As I understand, WeakKeyDict entries are supposed to be removed when the keys (particular instances of a type) get garbage collected. I think that would not work reliably if WeakRefs to two different instances produce the same hash value. Shouldn't the hash value of WeakRef be based on object ids of the referenced value instead of the value itself? Or may be WeakKeyDict should be implemented differently? |
I think both combinations make sense. One case where you want the current behavior of I could modify |
I feel like this should ideally be more composable:
etc. Not entirely sure if this is possible at the moment, but it seems like it's pretty close to doable at least. |
I realize this may make weak-key dicts less convenient to use since you'd have to both look each item up and dereference the actual value from the WeakRef container, but I feel like that's acceptable – it's not like WeakKeyDicts are something that ought to be used every day. |
But then that results in the following scenario:
I also noticed the usage of WeakKeyDict in multi.jl for RemoteRefs relies on the current behavior. But I wonder if it is correct... It seems to be using this behavior to get the same RemoteRef object instance in the constructor. |
The RemoteRef constructor works because it uses The error is an implementation bug. It can be fixed several ways; simplest is just to tolerate missing keys in the finalizer that calls |
Thanks. That sounds good. I think then a WeakKeyObjectIdDict is what would be useful as you suggested. I would be using this in the ChainedVectors package... where I try to create sub vectors of larger buffers without making a copy and need the larger buffer to hang around till any of the sub vectors are still being used. |
Here's an implementation of WeakObjectIdKeyDict, in the same lines as WeakKeyDict. Does it look good? type WeakObjectIdKeyDict{K,V} <: Associative{K,V}
ht::Dict{Uint,Tuple}
WeakObjectIdKeyDict() = new((Uint=>Tuple)[])
end
WeakObjectIdKeyDict() = WeakObjectIdKeyDict{Any,Any}()
function setindex!{K,V}(wih::WeakObjectIdKeyDict{K,V}, v, key)
oid = object_id(key::K)
wih.ht[oid] = (WeakRef(key), v::V)
finalizer(key, x->delete!(wih.ht, oid, nothing))
end
function getkey{K}(wih::WeakObjectIdKeyDict{K}, kk, deflt)
k = getkey(wih.ht, object_id(kk::K), secret_table_token)
if is(k, secret_table_token)
return deflt
end
kk
end
function get{K}(wih::WeakObjectIdKeyDict{K}, key, def)
v = get(wih.ht, object_id(key::K), def)
is(v, def) && return v
v[2]
end
delete!{K}(wih::WeakObjectIdKeyDict{K}, key) = delete!(wih.ht, object_id(key::K))[2]
function delete!{K}(wih::WeakObjectIdKeyDict{K}, key, def)
v = delete!(wih.ht, object_id(key::K), def)
is(v, def) && return v
v[2]
end
empty!(wih::WeakObjectIdKeyDict) = (empty!(wih.ht); wih)
haskey{K}(wih::WeakObjectIdKeyDict{K}, key) = haskey(wih.ht, object_id(key::K))
getindex{K}(wih::WeakObjectIdKeyDict{K}, key) = getindex(wih.ht, object_id(key::K))[2]
isempty(wih::WeakObjectIdKeyDict) = isempty(wih.ht)
start(wih::WeakObjectIdKeyDict) = start(wih.ht)
done(wih::WeakObjectIdKeyDict, i) = done(wih.ht, i)
function next{K}(wih::WeakObjectIdKeyDict{K}, i)
kv, i = next(wih.ht, i)
vv = kv[2]
((vv[1].value::K, vv[2]), i)
end
length(wih::WeakObjectIdKeyDict) = length(wih.ht) [pao: syntax highlighting] |
Looks good. It's interesting to note that for most purposes the key doesn't actually need to be stored in the dictionary; it's only needed in |
Yes. Do you feel we should skip the iterator in favor of efficiency? |
Another thing which seems surprising to me is: julia> w1,w2 = WeakRef([1]), WeakRef([1])
(WeakRef([1]), WeakRef([1]))
julia> w1==w2
true
julia> r1,r2 = Ref([1]), Ref([1])
(Base.RefArray{Int64,Array{Int64,1},Void}([1], 1, nothing), Base.RefArray{Int64,Array{Int64,1},Void}([1], 1, nothing))
julia> r1==r2
false WeakRef compares their values with I would argue that it would be clearer for EDIT: x-ref I came across #12198 (comment) |
I just tried bulding Julia with diff --git a/base/gcutils.jl b/base/gcutils.jl
index 0ff53bceb6..0f26be2af7 100644
--- a/base/gcutils.jl
+++ b/base/gcutils.jl
@@ -1,8 +1,8 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
-==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value)
-==(w::WeakRef, v) = isequal(w.value, v)
-==(w, v::WeakRef) = isequal(w, v.value)
+# ==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value)
+# ==(w::WeakRef, v) = isequal(w.value, v)
+# ==(w, v::WeakRef) = isequal(w, v.value)
"""
finalizer(f, x)
diff --git a/base/hashing.jl b/base/hashing.jl
index e2847342a8..a415f85a5b 100644
--- a/base/hashing.jl
+++ b/base/hashing.jl
@@ -14,7 +14,7 @@ Typically, any type that implements `hash` should also implement its own `==` (h
`isequal`) to guarantee the property mentioned above.
"""
hash(x::Any) = hash(x, zero(UInt))
-hash(w::WeakRef, h::UInt) = hash(w.value, h)
+# hash(w::WeakRef, h::UInt) = hash(w.value, h)
## hashing general objects ## It builds fine and the only error in the test-set is from deserializing a function:
So at least a WeakRef test should be added, which explicitly tests the |
Is this the correct behaviour?
Since v1 and v2 are distinct, shouldn't hash(w1) and hash(w2) be different for WeakKeyDict to work correctly?
The text was updated successfully, but these errors were encountered: