[Pharo-project] [squeak-dev] Re: WeakRegistry>>remove: - when you'll be in trouble

Levente Uzonyi leves at elte.hu
Mon Oct 11 01:51:12 UTC 2010


On Mon, 11 Oct 2010, Igor Stasenko wrote:

> 2010/10/11 Levente Uzonyi <leves at elte.hu>:
>> On Mon, 11 Oct 2010, Igor Stasenko wrote:
>>
>>> 2010/10/11 Levente Uzonyi <leves at elte.hu>:
>>> On Mon, 11 Oct 2010, Igor Stasenko wrote:
>>>
>>>> On 10 October 2010 23:43, Levente Uzonyi <leves at elte.hu> wrote:
>>>> On Sun, 10 Oct 2010, Igor Stasenko wrote:
>>>>
>>>>> A current implementation of this method
>>>>>
>>>>> remove: oldObject ifAbsent: exceptionBlock
>>>>>        "Remove oldObject as one of the receiver's elements."
>>>>>
>>>>>        oldObject ifNil: [ ^nil ].
>>>>>        ^(self protected: [ valueDictionary removeKey: oldObject
>>>>> ifAbsent:
>>>>> nil ])
>>>>>                ifNil: [ exceptionBlock value ]
>>>>>
>>>>> simply removes a previously registered object from registry and voila.
>>>>>
>>>>> Now lets get back to our discussion about multiple finalizers per
>>>>> object and using them in weak subscriptions etc.
>>>>>
>>>>> Suppose i am added a socket to weak registry,
>>>>> and suppose i am added a weak subscription to it.
>>>>>
>>>>> Now, if i do 'socket close' , it tells weak registry to remove it from
>>>>> list.
>>>>> And what we'll have:
>>>>> - socket handle is closed
>>>>> - socket is wiped from weak registry
>>>>> - but weak subscription still listed somewhere in a list of
>>>>> subscriptions
>>>>>
>>>>>
>>>>> My suggestion is, that upon #remove:,
>>>>> a weak registry should notify all executors that object of interest
>>>>> are no longer takes part in finalization scheme,
>>>>> so they should not count on receiving #finalize eventually.
>>>>>
>>>>> In other words:
>>>>>
>>>>> remove: oldObject ifAbsent: exceptionBlock
>>>>>        "Remove oldObject as one of the receiver's elements."
>>>>>
>>>>>        oldObject ifNil: [ ^nil ].
>>>>>        ^(self protected: [ | executor |
>>>>>            executor := valueDictionary removeKey: oldObject ifAbsent:
>>>>> nil.
>>>>>            executor discardFinalization.
>>>>>        ])
>>>>>        ifNil: [ exceptionBlock value ]
>>>>
>>>> It's only an issue with the new WeakRegistry implementation, previous
>>>> implementations don't have such problem. I think changing the method as
>>>> you
>>>> suggested, implementing WeakFinalizerItem >> #discardFinalization as
>>>> "executor := nil" and changing WeakFinalizerItem >> #finalizaValues to
>>>> ignore the executor if it's nil will fix the problem. Am I right?
>>>>
>>>> I don't get how "multiple finalizers per object" is related to this
>>>> problem
>>>> at all.
>>>>
>>>
>>> No, you miss the point.
>>> When you removing object from weak registry, it is important , time to
>>> time to tell all of its executors,
>>> that they will no longer receive #finalize, because object is no
>>> longer a member of weak registry.
>>>
>>> If you simply set executor := nil, it does nothing, an executor itself
>>> did not notified that he won't be needed
>>> in any possible future.
>>> So, if your finalization action is to remove some object(s) from the
>>> list , you'll get your list dirty after object will die,
>>> because #finalize will be never sent to your executor.
>>>
>>> Here the simple test case:
>>>
>>> | coll obj someWrapper |
>>> coll := OrderedCollection new.
>>> obj := Object new.
>>> someWrapper := WeakArray with: obj.
>>> coll add: someWrapper.
>>>
>>> obj toFinalizeSend: #remove: to: coll with: someWrapper.
>>> obj finalizationRegistry remove: obj.
>>> obj := nil.
>>> Smalltalk garbageCollect.
>>> self assert: coll isEmpty
>>>
>>>
>>> the point is, that once you doing a #remove: ,
>>> your finalization scheme is broken.
>>>
>>>
>>> I don't get you. When you remove an object from a WeakRegistry, you tell
>>> the
>>> system that you want the object removed and all related executors deleted.
>>> So the executors should never receive #finalize, they should be thrown
>>> away.
>>> Even if there's action to be done with the executors (which is pointless
>>> IMHO since those will be thrown away, but who knows), doing them is the
>>> responsibility of the sender of #remove:.
>>>
>>
>> Let us get back to multiple finalizers per object,
>> when you have two separate places, which adding possibly different
>> executors for a single object
>> without knowing about each other (otherwise why would you want to add
>> two finalizers instead of one).
>> Now if one decides to do #remove: ,
>> how to ensure that second one won't be left with dirty/invalid state?
>>
>>
>> It's not ensured, and it doesn't have to be. The sender of #remove: takes
>> all responsibility. It can decide to add some executors back to the registry
>> if that's necessary.
>> If you want to keep your executors safe from some other code, which may
>> #remove: your objects from a WeakRegistry, then you can create a private
>> WeakRegistry and store your executors there, just like Sockets and
>> FileStreams do.
>>
>
> Conclusion: weak registry don't have to support multiple finalizers
> per single object.
> Instead, if one sees the need to control a finalization of certain
> object(s) in own way,
> indepentently fron any other, then he should use a private weak
> registry (like Sockets and FileStreams do),
> and register object and its executor there.

The conclusion is that there is code out there which relies on having 
multiple finalizers per object. Even if that code uses a private 
WeakRegistry, one finalizer is not enough.

Btw, what's your problem with multiple finalizers per object?


Levente

>
> -- 
> Best regards,
> Igor Stasenko AKA sig.
>
>


More information about the Squeak-dev mailing list