Skip to content

Commit

Permalink
WeakKeyDict with no conversion on key-insertion (JuliaLang#24941) (Ju…
Browse files Browse the repository at this point in the history
  • Loading branch information
mauro3 authored and StefanKarpinski committed Jul 22, 2018
1 parent 04db1bd commit 41e749f
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 15 deletions.
8 changes: 5 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,9 @@ This section lists changes that do not have deprecation warnings.
This change makes `@schedule` redundant with `@async`, so `@schedule` has been
deprecated ([#27164]).

* `norm(A::AbstractMatrix, p=2)` computes no longer the operator/matrix norm but the `norm` of `A`
as for other iterables, i.e. as if it were a vector. Especially, `norm(A::AbstractMatrix)` is the
Frobenius norm. To compute the operator/matrix norm, use the new function `opnorm` ([#27401]).
* `norm(A::AbstractMatrix, p=2)` computes no longer the operator/matrix norm but the `norm` of `A`
as for other iterables, i.e. as if it were a vector. Especially, `norm(A::AbstractMatrix)` is the
Frobenius norm. To compute the operator/matrix norm, use the new function `opnorm` ([#27401]).

* `dot(u, v)` now acts recursively. Instead of `sum(u[i]' * v[i] for i in ...)`, it computes
`sum(dot(u[i], v[i]) for i in ...)`, similarly to `vecdot` before ([#27401]).
Expand All @@ -532,6 +532,8 @@ This section lists changes that do not have deprecation warnings.
cores present on the CPU. Similarly, the environment variable `JULIA_CPU_CORES` is
deprecated in favor of `JULIA_CPU_THREADS` ([#27856]).

* `WeakKeyDict` does not convert keys on insertion anymore (#24941).

Library improvements
--------------------

Expand Down
19 changes: 13 additions & 6 deletions base/weakkeydict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
references to objects, and thus may be garbage collected even when
referenced in a hash table.
See [`Dict`](@ref) for further help.
See [`Dict`](@ref) for further help. Note, unlike [`Dict`](@ref),
`WeakKeyDict` does not convert keys on insertion.
"""
mutable struct WeakKeyDict{K,V} <: AbstractDict{K,V}
ht::Dict{WeakRef,V}
Expand Down Expand Up @@ -75,10 +76,10 @@ lock(f, wkh::WeakKeyDict) = lock(f, wkh.lock)
trylock(f, wkh::WeakKeyDict) = trylock(f, wkh.lock)

function setindex!(wkh::WeakKeyDict{K}, v, key) where K
k = convert(K, key)
finalizer(wkh.finalizer, k)
!isa(key, K) && throw(ArgumentError("$key is not a valid key for type $K"))
finalizer(wkh.finalizer, key)
lock(wkh) do
wkh.ht[WeakRef(k)] = v
wkh.ht[WeakRef(key)] = v
end
return wkh
end
Expand All @@ -93,8 +94,14 @@ end

get(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> get(wkh.ht, key, default), wkh)
get(default::Callable, wkh::WeakKeyDict{K}, key) where {K} = lock(() -> get(default, wkh.ht, key), wkh)
get!(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> get!(wkh.ht, key, default), wkh)
get!(default::Callable, wkh::WeakKeyDict{K}, key) where {K} = lock(() -> get!(default, wkh.ht, key), wkh)
function get!(wkh::WeakKeyDict{K}, key, default) where {K}
!isa(key, K) && throw(ArgumentError("$key is not a valid key for type $K"))
lock(() -> get!(wkh.ht, WeakRef(key), default), wkh)
end
function get!(default::Callable, wkh::WeakKeyDict{K}, key) where {K}
!isa(key, K) && throw(ArgumentError("$key is not a valid key for type $K"))
lock(() -> get!(default, wkh.ht, WeakRef(key)), wkh)
end
pop!(wkh::WeakKeyDict{K}, key) where {K} = lock(() -> pop!(wkh.ht, key), wkh)
pop!(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> pop!(wkh.ht, key, default), wkh)
delete!(wkh::WeakKeyDict, key) = lock(() -> delete!(wkh.ht, key), wkh)
Expand Down
2 changes: 2 additions & 0 deletions doc/src/base/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ two functions for custom types to override how they are stored in a hash table.

[`WeakKeyDict`](@ref) is a hash table implementation where the keys are weak references to objects, and
thus may be garbage collected even when referenced in a hash table.
Like `Dict` it uses `hash` for hashing and `isequal` for equality, unlike `Dict` it does
not convert keys on insertion.

[`Dict`](@ref)s can be created by passing pair objects constructed with `=>` to a [`Dict`](@ref)
constructor: `Dict("A"=>1, "B"=>2)`. This call will attempt to infer type information from the
Expand Down
23 changes: 17 additions & 6 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -783,12 +783,6 @@ Dict(1 => rand(2,3), 'c' => "asdf") # just make sure this does not trigger a dep
A = [1]
B = [2]
C = [3]
local x = 0
local y = 0
local z = 0
finalizer(a->(x+=1), A)
finalizer(b->(y+=1), B)
finalizer(c->(z+=1), C)

# construction
wkd = WeakKeyDict()
Expand Down Expand Up @@ -830,10 +824,27 @@ Dict(1 => rand(2,3), 'c' => "asdf") # just make sure this does not trigger a dep

@test_throws ArgumentError WeakKeyDict([1, 2, 3])

wkd = WeakKeyDict(A=>1)
@test delete!(wkd, A) == empty(wkd)

# issue #26939
d26939 = WeakKeyDict()
d26939[big"1.0" + 1.1] = 1
GC.gc() # make sure this doesn't segfault

# WeakKeyDict does not convert keys on setting
@test_throws ArgumentError WeakKeyDict{Vector{Int},Any}([5.0]=>1)
wkd = WeakKeyDict(A=>2)
@test_throws ArgumentError get!(wkd, [2.0], 2)
@test_throws ArgumentError get!(wkd, [1.0], 2) # get! fails even if the key is only
# used for getting and not setting

# WeakKeyDict does convert on getting
wkd = WeakKeyDict(A=>2)
@test keytype(wkd)==Vector{Int}
@test wkd[[1.0]] == 2
@test haskey(wkd, [1.0])
@test pop!(wkd, [1.0]) == 2
end

@testset "issue #19995, hash of dicts" begin
Expand Down

0 comments on commit 41e749f

Please sign in to comment.