[squeak-dev] Priority of WeakArray finalizationProcess

Levente Uzonyi leves at elte.hu
Mon Jun 14 20:06:51 UTC 2010


On Mon, 14 Jun 2010, Joachim Geidel wrote:

> Am 13.06.10 20:35 schrieb Levente Uzonyi:
>
>> If I select JVMWithoutCallbacks(JVM)>>own: in the debugger and inspect the
>> instance variable "sharedMutex", I find that the UI process is still the
>> owner of the mutex, the mutex's semaphore has no signals and the process
>> is in the list of the semaphore. But the process is not in the critical
>> section of the mutex, because the block containing the #critical: send is
>> already evaluated (JNIPortWeakRegistry>>registerValueOf:). So the mutex is
>> "locked", but it's not used by the process that "locks" it.
>> The deadlock occurs, because the finalization process is trying to access
>> the same mutex to finalize the objects, while it's also in the critical
>> section of the WeakRegistry's semaphore. While this is happening, the UI
>> process is trying to register new objects to the WeakRegistry.
>> I couldn't find out how can the Mutex have this invalid state.
>
> I am going to look into this, but this week I am quite busy, so it may take
> some time.
>
> However, I think that it's unsafe to send #finalize to the executors while
> WeakRegistry is still in the protected block in finalizeValues. You don't
> have control over what finalize is doing, and this can lead to deadlocks if
> a #finalize waits on a Semaphore. Would it be possible to collect the
> objects to be finalized in a collection in the protected block, and finalize
> them afterwards?

After looking at the deadlock again, I found that it's a simple 2 process 
- 2 lock circular wait deadlock.

Here is the stack trace of the user process (which isn't the UI process 
as I called it previously):
[] in Semaphore>>critical:
BlockClosure>>ensure:
Semaphore>>critical:
Semaphore>>critical:ifError:
JNIPortWeakRegistry(WeakRegistry)>>protected:
JNIPortWeakRegistry(WeakRegistry)>>add:executor:
JNIPortWeakRegistry(WeakRegistry)>>add:
JNIPortWeakRegistry>>registerValueOf:
JVMWithoutCallbacks(JVM)>>own:
JavaLangReflectMethod(JavaClassInstance)>>jniObject:static:
JavaLangReflectMethod class(JavaClassInstance class)>>jniObject:static:
StaticJavaLangObject(JavaClassStatic)>>basicWrapJNIObject:
[] in StaticJavaLangObject(JavaClassStatic)>>wrapJNIObject:
JavaObjectRegistry>>basicFindOrRelease:ifAbsentPut:
[] in JavaObjectRegistry>>findOrRelease:ifAbsentPut:
BlockClosure>>ensure:
[] in Mutex>>critical:
[] in Semaphore>>critical:
BlockClosure>>ensure:
Semaphore>>critical:

And here is the finalization process' stack trace:
[] in Semaphore>>critical:
BlockClosure>>ensure:
Semaphore>>critical:
Mutex>>critical:
JVMWithoutCallbacks(JVM)>>disown:
JavaLangReflectMethod(JavaClassInstance)>>free
JavaLangReflectMethod(JavaClassInstance)>>finalize
ByteSymbol(Symbol)>>value:
WeakIdentityKeyDictionary(WeakKeyDictionary)>>finalizeValues
[] in JNIPortWeakRegistry(WeakRegistry)>>finalizeValues
[] in [] in Semaphore>>critical:ifError:
BlockClosure>>on:do:
BlockClosure>>ifError:
[] in Semaphore>>critical:ifError:
[] in Semaphore>>critical:
BlockClosure>>ensure:
Semaphore>>critical:
Semaphore>>critical:ifError:
JNIPortWeakRegistry(WeakRegistry)>>protected:
JNIPortWeakRegistry(WeakRegistry)>>finalizeValues

So the user process entered the mutex of the JVM and it's trying to enter 
the WeakRegistry's semaphore, while the finalization process entered the 
WeakRegistry's semaphore and it's trying to enter the JVM's mutex.

(The mutex's owner was is not nil when the deadlock happens, because the 
code enters the mutex more than once, so that's normal.)

This deadlock doesn't happen in Pharo, because it uses the old 
WeakRegistry >> #finalizeValues implementation which does the actual 
finalization outside the critical section. I will soon upload a version of 
the Collections package to the Inbox which does the same. With this code 
it took 2.9 seconds to start the JVM on my notebook.


Levente

>
> Joachim
>
>
>
>



More information about the Squeak-dev mailing list