Argh!  This one:   :)

|d|
d:={('hello' copy) -> 'world'.  'hello2' copy -> 'there'} as: WeakKeyDictionary.
Smalltalk garbageCollect.
self assert: (d includesKey: 'hello') not.   "good"
self assert: d notEmpty.   "bad"
self assert: d array asSet size > 1.   "bad"
d finalizeValues.   "required!"
self assert: d array asSet size = 1.   "good"
self assert: d isEmpty.   "good"
self assert: (d reject: [ : each | each isNil ]) isEmpty.   "alternative to finalizeValues"


On Tue, May 31, 2022 at 7:12 PM Chris Muller <asqueaker@gmail.com> wrote:
Please replace the script in my prior email with this one.  Even though it still illustrates the problem, the previous script has a conceptual bug because I was playing around with Identity vs. regular and forgot to correct it back.

|d|
d:={('hello' copy) -> 'world'.  'hello2' copy -> 'there'} as: WeakKeyDictionary.
Smalltalk garbageCollect.
self assert: (d includesKey: obj1) not.
self assert: d notEmpty.
self assert: d array asSet size > 1.
d finalizeValues.
self assert: d array asSet size = 1.
self assert: d isEmpty.
self assert: (d reject: [ : each | each isNil ]) isEmpty.


On Tue, May 31, 2022 at 7:04 PM Chris Muller <asqueaker@gmail.com> wrote:
Hi Jakob, hi Levente,

> Whenever I read the class comment of WeakKeyDictionary, I know that I have to take care of something, but I do not know what. Can we improve the comment please to tell the readers more directly what is expected of them?
>
> - Must one ensure that #finalizeValues is sent to one's WeakKeyDictionary instance regularly?

Yes.  Either that, or you need to otherwise somehow remove the garbage collected entries from its internal array.

Otherwise, the WeakKeyDictionary will only ever grow in size internally, with a bunch of nil keys.  Here's a short script showing the mechanics and assumptions of this:

|d|
d:={'hello' copy -> 'world'.  'hello' copy -> 'there'} as: WeakIdentityKeyDictionary.
Smalltalk garbageCollect.
self assert: (d includesKey: 'hello') not.   "good"
self assert: d notEmpty.    "bad"
self assert: d array asSet size > 1.   "bad"
d finalizeValues.   "required!"
self assert: d array asSet size = 1.   "good"
self assert: d isEmpty.    "good"
 
Only if you are interested in finalization and want to do it your own way.
And even then, only if you want to have your finalizer executed as soon as
possible when a key of your dictionary has been garbage collected.
So, if you just want a dictionary with weak keys, the answer is no.

> - Should one register the dictionary instance via WeakArray addWeakDependent:?

Same as above. I'm not even sure it would work with
WeakKeyDictionaries. It's intended to work with WeakRegistries, which are
the main providers of finalization.

I believe it would.  Check out WeakArray class>>#finalizationProcess and, perhaps doing so would allow those WeakKey dictionary's to clean themselves up automatically so you don't have to time sending #finalizeValues yourself.  But, I don't trust it, because it's hard to tell, because #finalizationProcess waits on FinalizationSemaphore, which is an opaque VM-controlled semaphore which I have no idea when it's signaled.
  
> - Which VM feature is the WeakRegistry class comment referring to; is it the semaphore used on the class side of WeakArray; and is it relevant to the use of WeakKeyDictionary?

One[1] that has never really been used in Squeak and has been
obsoleted by ephemerons. Even though we do not use ephemerons for
finalization yet.

> - If the answers to the questions above do not give it away, what must one do to use a WeakKeyDictionary safely?

The difference between a regular Dictionary and a WeakKeyDictionary is
that the latter cannot hold nil as a key, and your associations may
disappear when their keys are not referenced strongly.

They only disappear from the perspective of using the object API (excluding #isEmpty and #notEmpty as demonstrated by the script above).  Internally, the former Association objects don't disappear, and still take up memory.

Igor made some alternative-implementation WeakKeyDictionary's for Magma back in 2007 which are faster than the one's built into Squeak to this day (last I checked, anyway).  If interested, load "MaBase" from SqueakMap and then see MaWeakIdentityKeyDictionary.

Regards,
  Chris