Hello everyone,
in EphemeronDictionary (and HandleRegistry), some methods are protected by #evaluateWithFullProtection:, while other methods directly use "accessLock critical: [...]". I would like to implement a Weakling-based WeakSet (final destination Public Repository, of course ;-) ), where I need to use the same technique. When an exception is raised in a block protected by the accessLock, and the exception is not handled, all other processes accessing the EphemeronDictionary are blocked until the situation is resolved. If the block is protected by #evaluateWithFullProtection:, the RecursionLock would be available immediately, letting other Processes access the EphemeronDictionary again. (Please correct me if I'm wrong.) #at:ifAbsent:, #associationAt:ifAbsent: and #removeKey:ifAbsent: use the accessLock directly. That makes sense, because an Exception in the ifAbsent-block could be handled elsewhere and resume or restart the execution of the block, which should still occur in the protected section (the exception handler and/or the ifAbsent-block might want to change the EphemeronDictionary). But shouldn't the methods #associations, #includesKey: and #localBindings use #evaluateWithFullProtection:? And why does WeakDictionary>>associationsDo: use "accessLock critical:" while EphemeronDictionary>>associationsDo: uses #evaluateWithFullProtection:? I would expect that all the query methods use #evaluateWithFullProtection: while all methods which might change the dictionary (including all methods which have a block as an argument) should use "accessLock critical:". Is that reasonable? I'm also wondering about WeakDictionary>>setAccessLock:. When a WeakDictionary is growing, it makes sure that it uses the same accessLock afterwards. There is no corresponding code in EphemeronDictionary. When it grows, it will use a new RecursionLock afterwards. Could this cause problems during the transition from the old to the new dictionary? It seems that there is a bug in EphemeronDictionary>>changeCapacityTo: In this method, #associationsDo: is protecting the original self, but the protection does not extend to the final #become:. If finalization happens to mourn an Ephemeron key just between copying the associations and the become:, then the new copy will still contain the now obsolete Ephemeron. If the VM does not detect that it is still there (after all, it believes it has already dealt with it), then the Ephemeron will hang around forever. I don't have the VM code to check this. The problem, if it actually exists, might cause memory leaks in DependentsFields, EventHandlers and the SubscriptionRegistry of the Announcement framework. The following code illustrates the problem: | dict newDict key ephemeron | dict := EphemeronDictionary new. key := String new. dict at: key put: 1. ephemeron := dict associationAt: key. key := nil. "dereference it, probably not necessary here" "simulate #changeCapacityTo:" newDict := dict copyEmpty: 10. dict associationsDo: [:each | newDict noCheckAdd: each]. "simulate finalization during #changeCapacityTo:" ephemeron mourn. dict become: newDict. "see what's left" dict associations "OrderedCollection (''->1) - still there" newDict associations "OrderedCollection () - this is the old self" Best regards, Joachim Geidel |
Good questions, to which I do not know the answers. However, I've created AR 50998 for the two issues you raise at the end of the message.
At 08:29 AM 7/15/2006, Joachim Geidel wrote: >Hello everyone, > >in EphemeronDictionary (and HandleRegistry), some methods are protected >by #evaluateWithFullProtection:, while other methods directly use >"accessLock critical: [...]". I would like to implement a Weakling-based >WeakSet (final destination Public Repository, of course ;-) ), where I >need to use the same technique. > >When an exception is raised in a block protected by the accessLock, and >the exception is not handled, all other processes accessing the >EphemeronDictionary are blocked until the situation is resolved. If the >block is protected by #evaluateWithFullProtection:, the RecursionLock >would be available immediately, letting other Processes access the >EphemeronDictionary again. (Please correct me if I'm wrong.) > >#at:ifAbsent:, #associationAt:ifAbsent: and #removeKey:ifAbsent: use the >accessLock directly. That makes sense, because an Exception in the >ifAbsent-block could be handled elsewhere and resume or restart the >execution of the block, which should still occur in the protected >section (the exception handler and/or the ifAbsent-block might want to >change the EphemeronDictionary). But shouldn't the methods >#associations, #includesKey: and #localBindings use >#evaluateWithFullProtection:? And why does >WeakDictionary>>associationsDo: use "accessLock critical:" while >EphemeronDictionary>>associationsDo: uses #evaluateWithFullProtection:? > >I would expect that all the query methods use >#evaluateWithFullProtection: while all methods which might change the >dictionary (including all methods which have a block as an argument) >should use "accessLock critical:". Is that reasonable? > >I'm also wondering about WeakDictionary>>setAccessLock:. When a >WeakDictionary is growing, it makes sure that it uses the same >accessLock afterwards. There is no corresponding code in >EphemeronDictionary. When it grows, it will use a new RecursionLock >afterwards. Could this cause problems during the transition from the old >to the new dictionary? > >It seems that there is a bug in EphemeronDictionary>>changeCapacityTo: >In this method, #associationsDo: is protecting the original self, but >the protection does not extend to the final #become:. If finalization >happens to mourn an Ephemeron key just between copying the associations > and the become:, then the new copy will still contain the now obsolete >Ephemeron. If the VM does not detect that it is still there (after all, >it believes it has already dealt with it), then the Ephemeron will hang >around forever. I don't have the VM code to check this. The problem, if >it actually exists, might cause memory leaks in DependentsFields, >EventHandlers and the SubscriptionRegistry of the Announcement framework. > >The following code illustrates the problem: >| dict newDict key ephemeron | >dict := EphemeronDictionary new. >key := String new. >dict at: key put: 1. >ephemeron := dict associationAt: key. >key := nil. "dereference it, probably not necessary here" >"simulate #changeCapacityTo:" >newDict := dict copyEmpty: 10. >dict associationsDo: [:each | newDict noCheckAdd: each]. >"simulate finalization during #changeCapacityTo:" >ephemeron mourn. >dict become: newDict. >"see what's left" >dict associations "OrderedCollection (''->1) - still there" >newDict associations "OrderedCollection () - this is the old self" > >Best regards, >Joachim Geidel -- Alan Knight [|], Cincom Smalltalk Development [hidden email] [hidden email] http://www.cincom.com/smalltalk "The Static Typing Philosophy: Make it fast. Make it right. Make it run." - Niall Ross |
Free forum by Nabble | Edit this page |