@@ -10,24 +10,19 @@ references to objects which may be garbage collected even when
10
10
referenced in a hash table.
11
11
12
12
See [`Dict`](@ref) for further help. Note, unlike [`Dict`](@ref),
13
- `WeakKeyDict` does not convert keys on insertion.
13
+ `WeakKeyDict` does not convert keys on insertion, as this would imply the key
14
+ object was unreferenced anywhere before insertion.
14
15
"""
15
16
mutable struct WeakKeyDict{K,V} <: AbstractDict{K,V}
16
17
ht:: Dict{WeakRef,V}
17
18
lock:: ReentrantLock
18
19
finalizer:: Function
20
+ dirty:: Bool
19
21
20
22
# Constructors mirror Dict's
21
23
function WeakKeyDict {K,V} () where V where K
22
- t = new (Dict {Any,V} (), ReentrantLock (), identity)
23
- t. finalizer = function (k)
24
- # when a weak key is finalized, remove from dictionary if it is still there
25
- if islocked (t)
26
- finalizer (t. finalizer, k)
27
- return nothing
28
- end
29
- delete! (t, k)
30
- end
24
+ t = new (Dict {Any,V} (), ReentrantLock (), identity, 0 )
25
+ t. finalizer = k -> t. dirty = true
31
26
return t
32
27
end
33
28
end
@@ -69,56 +64,149 @@ function WeakKeyDict(kv)
69
64
end
70
65
end
71
66
67
+ function _cleanup_locked (h:: WeakKeyDict )
68
+ if h. dirty
69
+ h. dirty = false
70
+ idx = skip_deleted_floor! (h. ht)
71
+ while idx != 0
72
+ if h. ht. keys[idx]. value === nothing
73
+ _delete! (h. ht, idx)
74
+ end
75
+ idx = skip_deleted (h. ht, idx + 1 )
76
+ end
77
+ end
78
+ return h
79
+ end
80
+
72
81
sizehint! (d:: WeakKeyDict , newsz) = sizehint! (d. ht, newsz)
73
82
empty (d:: WeakKeyDict , :: Type{K} , :: Type{V} ) where {K, V} = WeakKeyDict {K, V} ()
74
83
84
+ IteratorSize (:: Type{<:WeakKeyDict} ) = SizeUnknown ()
85
+
75
86
islocked (wkh:: WeakKeyDict ) = islocked (wkh. lock)
76
87
lock (f, wkh:: WeakKeyDict ) = lock (f, wkh. lock)
77
88
trylock (f, wkh:: WeakKeyDict ) = trylock (f, wkh. lock)
78
89
79
90
function setindex! (wkh:: WeakKeyDict{K} , v, key) where K
80
91
! isa (key, K) && throw (ArgumentError (" $(limitrepr (key)) is not a valid key for type $K " ))
81
- finalizer (wkh. finalizer, key)
92
+ # 'nothing' is not valid both because 'finalizer' will reject it,
93
+ # and because we therefore use it as a sentinel value
94
+ key === nothing && throw (ArgumentError (" `nothing` is not a valid WeakKeyDict key" ))
82
95
lock (wkh) do
83
- wkh. ht[WeakRef (key)] = v
96
+ _cleanup_locked (wkh)
97
+ k = getkey (wkh. ht, key, nothing )
98
+ if k === nothing
99
+ finalizer (wkh. finalizer, key)
100
+ k = WeakRef (key)
101
+ else
102
+ k. value = key
103
+ end
104
+ wkh. ht[k] = v
84
105
end
85
106
return wkh
86
107
end
108
+ function get! (wkh:: WeakKeyDict{K} , key, default) where {K}
109
+ v = lock (wkh) do
110
+ if key != = nothing && haskey (wkh. ht, key)
111
+ wkh. ht[key]
112
+ else
113
+ wkh[key] = default
114
+ end
115
+ end
116
+ return v
117
+ end
118
+ function get! (default:: Callable , wkh:: WeakKeyDict{K} , key) where {K}
119
+ v = lock (wkh) do
120
+ if key != = nothing && haskey (wkh. ht, key)
121
+ wkh. ht[key]
122
+ else
123
+ wkh[key] = default ()
124
+ end
125
+ end
126
+ return v
127
+ end
87
128
88
129
function getkey (wkh:: WeakKeyDict{K} , kk, default) where K
89
- return lock (wkh) do
90
- k = getkey (wkh. ht, kk, secret_table_token )
91
- k === secret_table_token && return default
92
- return k. value:: K
130
+ k = lock (wkh) do
131
+ k = getkey (wkh. ht, kk, nothing )
132
+ k === nothing && return nothing
133
+ return k. value
93
134
end
135
+ return k === nothing ? default : k:: K
94
136
end
95
137
96
- map! (f,iter:: ValueIterator{<:WeakKeyDict} )= map! (f, values (iter. dict. ht))
97
- get (wkh:: WeakKeyDict{K} , key, default) where {K} = lock (() -> get (wkh. ht, key, default), wkh)
98
- get (default:: Callable , wkh:: WeakKeyDict{K} , key) where {K} = lock (() -> get (default, wkh. ht, key), wkh)
99
- function get! (wkh:: WeakKeyDict{K} , key, default) where {K}
100
- ! isa (key, K) && throw (ArgumentError (" $(limitrepr (key)) is not a valid key for type $K " ))
101
- lock (() -> get! (wkh. ht, WeakRef (key), default), wkh)
138
+ map! (f, iter:: ValueIterator{<:WeakKeyDict} )= map! (f, values (iter. dict. ht))
139
+
140
+ function get (wkh:: WeakKeyDict{K} , key, default) where {K}
141
+ key === nothing && throw (KeyError (nothing ))
142
+ lock (wkh) do
143
+ return get (wkh. ht, key, default)
144
+ end
102
145
end
103
- function get! (default:: Callable , wkh:: WeakKeyDict{K} , key) where {K}
104
- ! isa (key, K) && throw (ArgumentError (" $(limitrepr (key)) is not a valid key for type $K " ))
105
- lock (() -> get! (default, wkh. ht, WeakRef (key)), wkh)
146
+ function get (default:: Callable , wkh:: WeakKeyDict{K} , key) where {K}
147
+ key === nothing && throw (KeyError (nothing ))
148
+ lock (wkh) do
149
+ return get (default, wkh. ht, key)
150
+ end
151
+ end
152
+ function pop! (wkh:: WeakKeyDict{K} , key) where {K}
153
+ key === nothing && throw (KeyError (nothing ))
154
+ lock (wkh) do
155
+ return pop! (wkh. ht, key)
156
+ end
157
+ end
158
+ function pop! (wkh:: WeakKeyDict{K} , key, default) where {K}
159
+ key === nothing && return default
160
+ lock (wkh) do
161
+ return pop! (wkh. ht, key, default)
162
+ end
163
+ end
164
+ function delete! (wkh:: WeakKeyDict , key)
165
+ key === nothing && return wkh
166
+ lock (wkh) do
167
+ delete! (wkh. ht, key)
168
+ end
169
+ return wkh
170
+ end
171
+ function empty! (wkh:: WeakKeyDict )
172
+ lock (wkh) do
173
+ empty! (wkh. ht)
174
+ end
175
+ return wkh
176
+ end
177
+ function haskey (wkh:: WeakKeyDict{K} , key) where {K}
178
+ key === nothing && return false
179
+ lock (wkh) do
180
+ return haskey (wkh. ht, key)
181
+ end
182
+ end
183
+ function getindex (wkh:: WeakKeyDict{K} , key) where {K}
184
+ key === nothing && throw (KeyError (nothing ))
185
+ lock (wkh) do
186
+ return getindex (wkh. ht, key)
187
+ end
188
+ end
189
+ isempty (wkh:: WeakKeyDict ) = length (wkh) == 0
190
+ function length (t:: WeakKeyDict )
191
+ lock (t) do
192
+ _cleanup_locked (t)
193
+ return length (t. ht)
194
+ end
106
195
end
107
- pop! (wkh:: WeakKeyDict{K} , key) where {K} = lock (() -> pop! (wkh. ht, key), wkh)
108
- pop! (wkh:: WeakKeyDict{K} , key, default) where {K} = lock (() -> pop! (wkh. ht, key, default), wkh)
109
- delete! (wkh:: WeakKeyDict , key) = (lock (() -> delete! (wkh. ht, key), wkh); wkh)
110
- empty! (wkh:: WeakKeyDict ) = (lock (() -> empty! (wkh. ht), wkh); wkh)
111
- haskey (wkh:: WeakKeyDict{K} , key) where {K} = lock (() -> haskey (wkh. ht, key), wkh)
112
- getindex (wkh:: WeakKeyDict{K} , key) where {K} = lock (() -> getindex (wkh. ht, key), wkh)
113
- isempty (wkh:: WeakKeyDict ) = isempty (wkh. ht)
114
- length (t:: WeakKeyDict ) = length (t. ht)
115
196
116
197
function iterate (t:: WeakKeyDict{K,V} , state... ) where {K, V}
117
- y = lock (() -> iterate (t. ht, state... ), t)
118
- y === nothing && return nothing
119
- wkv, newstate = y
120
- kv = Pair {K,V} (wkv[1 ]. value:: K , wkv[2 ])
121
- return (kv, newstate)
198
+ return lock (t) do
199
+ while true
200
+ y = iterate (t. ht, state... )
201
+ y === nothing && return nothing
202
+ wkv, state = y
203
+ k = wkv[1 ]. value
204
+ GC. safepoint () # ensure `k` is now gc-rooted
205
+ k === nothing && continue # indicates `k` is scheduled for deletion
206
+ kv = Pair {K,V} (k:: K , wkv[2 ])
207
+ return (kv, state)
208
+ end
209
+ end
122
210
end
123
211
124
212
filter! (f, d:: WeakKeyDict ) = filter_in_one_pass! (f, d)
0 commit comments