Re: [Seaside] WeakArray (again)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
23 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: [Seaside] WeakArray (again)

cdavidshaffer
oops...

'From Squeak3.7 of ''4 September 2004'' [latest update: #5989] on 26 March 2006 at 12:56:17 am'!

!WeakArray class methodsFor: 'private' stamp: 'cds 3/25/2006 15:01'!
compactFinalizationDependents: minimumSize
        | tmp index |
        tmp := WeakArray new: minimumSize + 10.
        index := 1.
        FinalizationLock
                critical: [FinalizationDependents
                                do: [:dependent | dependent
                                                ifNotNil: [tmp at: index put: dependent.
                                                        index := index + 1]].
                        FinalizationDependents := tmp]
                ifError: [:msg :rcvr | rcvr error: msg]! !

!WeakArray class methodsFor: 'private' stamp: 'cds 3/26/2006 00:56'!
finalizationProcess
        | nilEntries |
        [true] whileTrue:
                [nilEntries := 0.
                FinalizationSemaphore wait.
                FinalizationLock critical:
                        [FinalizationDependents do:
                                [:weakDependent |
                                weakDependent ifNotNil:
                                        [weakDependent finalizeValues.
                                        "***Following statement is required to keep weakDependent
                                        from holding onto its value as garbage.***"
                                        weakDependent _ nil]
                                        ifNil: [nilEntries := nilEntries + 1]]]
                        ifError:
                        [:msg :rcvr | rcvr error: msg].

                "Check if we should compact the array"
                (nilEntries > (FinalizationDependents size quo: 4) and: [FinalizationDependents size > 10])
                        ifTrue: [self compactFinalizationDependents: (FinalizationDependents size - nilEntries)]
                ].
! !



Reply | Threaded
Open this post in threaded view
|

Re: Finalization

cdavidshaffer
In reply to this post by Andreas.Raab
I've started to make an attempt at this but I didn't get far....I've got
a class which mimics WeakValueAssociation but has nextLink and executor
ivars.

Andreas Raab wrote:

> signalFinalization: oop
>   (self fetchClassOf: oop) == self classFinalizer ifTrue:[
>     self addLastLink: oop toList: self finalizerList.
>     self forceInterruptCheck.
>     pendingFinalizationSignals _ pendingFinalizationSignals + 1.
>   ].
>
>
addLastLink:toList: is defined in Interpreter but the current
finalization code resides in ObjectMemory.  Should I

a) just send it anyway...yuk?
b) move it up?
c) duplicate it (with different name?) in ObjectMemory?

Also, how do I safely unlink elements from the "ReadyForFinalization"
list in Smalltalk?  We will always be removing from the front of the
list.  I looked at Process which simply sends #remove: but this seems
dangerous if a GC should occur during execution of this method (there is
a race condition related to the "firstLink"):

remove: aLink ifAbsent: aBlock
    "Remove aLink from the receiver. If it is not there, answer the
result of
    evaluating aBlock."

    | tempLink |
    aLink == firstLink
        ifTrue: [firstLink _ aLink nextLink.      "if GC happens here
then lastLink is invalid..."
                aLink == lastLink
                    ifTrue: [lastLink _ nil]]
        ifFalse: [tempLink _ firstLink.
                [tempLink == nil ifTrue: [^aBlock value].
                 tempLink nextLink == aLink]
                    whileFalse: [tempLink _ tempLink nextLink].
                tempLink nextLink: aLink nextLink.
                aLink == lastLink
                    ifTrue: [lastLink _ tempLink]].
    aLink nextLink: nil.
    ^aLink

> WeakRegistry and other users would be fairly straighforward to deal
> with - they'd just store (strong references to) Finalizer's instead of
> (weak) object references and the finalizer would remove itself from
> the registry. No big deal, really.


While _some_ users of "Weak" structures won't care about finalization,
it looks like many will.  The "dictionary-like" objects all rehash
whenever one of their keys is collected.  Seaside, in particular, does this:

    dict _ WeakIdentityKeyDictionary new: aNumber.
    WeakArray addWeakDependent: dict.

so that dict gets rehashed whenever needed.  All that we're doing now is
providing a hint as to who was affected (so we'll only rehash
dictionaries which had values reclaimed) but suppose, for example, that
the same dictionary had multiple collected keys, now instead of
rehashing once we'll have to be a bit cleaver or we're going to consider
rehashing for each reclaimed object.  That is, we'll run
WeakKeyDictionary>>finalizeValues for each collected object that
corresponds to a key in this dictionary.

Old Scheme
---------------------
Loops over all WeakArray's FinalizationDependents sending finalizeValues
since it doesn't know who cares about that object

New Scheme
-----------------------
Loops removing elements from Finalizable list running the executor for
each (these executors presumably send #finalizeValues to dictionary if
they correspond to a dictionary entry)

Since almost all of the of the elements of FinalizationDependents (in a
Seaside image) are WeakIdentityKeyDictionaries there will be some
savings but not as much as I had hoped and I think this method would be
particularly wasteful if there isn't some way to coalesce sends of
#finalizeValues.


David


Reply | Threaded
Open this post in threaded view
|

Re: Finalization

Andreas.Raab
David Shaffer wrote:
> addLastLink:toList: is defined in Interpreter but the current
> finalization code resides in ObjectMemory.  Should I
>
> a) just send it anyway...yuk?
> b) move it up?
> c) duplicate it (with different name?) in ObjectMemory?

a) Send it anyway.

ObjectMemory and Interpreter are always run together - the distinction
between them (while useful on a conceptual level) is irrelevant in practice.

> Also, how do I safely unlink elements from the "ReadyForFinalization"
> list in Smalltalk?  We will always be removing from the front of the
> list.  I looked at Process which simply sends #remove: but this seems
> dangerous if a GC should occur during execution of this method (there is
> a race condition related to the "firstLink"):

I think we'll need a primitive for that, since primitives are run
atomically (so there isn't a GC issue unless you create one ;-)

>> WeakRegistry and other users would be fairly straighforward to deal
>> with - they'd just store (strong references to) Finalizer's instead of
>> (weak) object references and the finalizer would remove itself from
>> the registry. No big deal, really.
>
> While _some_ users of "Weak" structures won't care about finalization,
> it looks like many will.  The "dictionary-like" objects all rehash
> whenever one of their keys is collected.  Seaside, in particular, does this:
>
>     dict _ WeakIdentityKeyDictionary new: aNumber.
>     WeakArray addWeakDependent: dict.
>
> so that dict gets rehashed whenever needed.  All that we're doing now is
> providing a hint as to who was affected (so we'll only rehash
> dictionaries which had values reclaimed) but suppose, for example, that
> the same dictionary had multiple collected keys, now instead of
> rehashing once we'll have to be a bit cleaver or we're going to consider
> rehashing for each reclaimed object.  That is, we'll run
> WeakKeyDictionary>>finalizeValues for each collected object that
> corresponds to a key in this dictionary.

Hell, no! What we'll do is instead of just sending #finalize is that
we'll send #finalize: with the finalizer as an argument (which by
default just invokes #finalize on the receiver) so that registries can
remove just the entry that was finalized. Something like here:

Object>>finalize: aFinalizer
    "The receiver, being the executor for the value stored in aFinalizer
is being asked to perform finalization. By default, we ignore the
argument and simply call #finalize; but subclasses can use that
information for good purpose."
   ^self finalize

WeakArray>>finalize: aFinalizer
     self removeEntry: aFinalizer. "you get the idea"

In the (absolute worst and unlikely) case that passing the finalizer
along isn't enough we can add an extra slot for an object receiving
notifications about the finalization (but it's probably unnecessary).

> Old Scheme
> ---------------------
> Loops over all WeakArray's FinalizationDependents sending finalizeValues
> since it doesn't know who cares about that object
>
> New Scheme
> -----------------------
> Loops removing elements from Finalizable list running the executor for
> each (these executors presumably send #finalizeValues to dictionary if
> they correspond to a dictionary entry)
>
> Since almost all of the of the elements of FinalizationDependents (in a
> Seaside image) are WeakIdentityKeyDictionaries there will be some
> savings but not as much as I had hoped and I think this method would be
> particularly wasteful if there isn't some way to coalesce sends of
> #finalizeValues.

Nope. It'll be something like a #removeKey: per finalized entry. I can
guarantee that - if it were different I wouldn't be interested at all ;-)

Cheers,
   - Andreas


12