RE: JavaMemoryModel: Java Class initialization can deadlock?

From: Boehm, Hans (hans_boehm@hp.com)
Date: Wed Mar 19 2003 - 16:27:42 EST


In my opinion, it would be nice if we ended up with a spec that fixed all significant threading issues in the base language. Thus I would think it's worth looking at this. I'm less sure that there's a lot that can be done about it.

I hadn't thought about the initialization rules very much before this. Here's an attempt at paraphrasing the rules at a higher level, with the goal of either shedding some light, or pointing out that I'm completely confused:

0) Class initializations are triggered by "active uses" uses of a class, i.e. uses that may depend on the state associated with the class.

1) We allow one class initialization to invoke other class initializations, in order to get the proper initialization order between classes.

2) Whenever we try to initialize a class that is already being initialized, we wait for the previous initialization attempt to complete.

3) This can obviously deadlock even with a single thread, so we patch this by detecting the most obvious deadlock case which involves a recursive invocation of class initialization from the same thread. In that case we allow the (partially) uninitialized class to be used instead. This causes some strange behavior, but since most classes presumably contain many methods that rely on only compile-time initialization, it allows some interesting cases to work correctly.

If this interpretation is correct, presumably the natural fix for the problem Bill pointed out is to strengthen the deadlock detection in step (3) and allow use of the uninitialized class for other deadlocks as well. I think that if all "recursive" uses of a class during its initialization rely only on compile-time initializations, this remains safe. Thus I'm not sure this makes it harder to write correct code than it already is, in spite of the fact that this sounds like more of a band-aid solution.

On the other hand, I think it's fairly arbitrary how far you strengthen the deadlock detection. Do you include deadlocks that include locks in addition to those that involve purely initialization waits as in Bill's example? How about Object.wait()? Spinning on a volatile?

Since there is no rule that prevents class initialization while holding locks, and class initialization may need locks, it seems to me that initialization always needs to be considered when reasoning about deadlocks. Is this example really worse than the other cases we can't avoid?

Hans

> -----Original Message-----
> From: Bill Pugh [mailto:pugh@cs.umd.edu]
> Sent: Tuesday, March 18, 2003 1:07 PM
> To: javamemorymodel@cs.umd.edu; Gilad Bracha
> Subject: JavaMemoryModel: Java Class initialization can deadlock?
>
>
> I believe that according to section 2.17.5 of the JVMS, java class
> initialization can deadlock.
>
> Consider:
>
> class A {
> static int x = 1;
> static {
> B.y = 1;
> }
> }
>
> class B {
> static int y = 2;
> static {
> A.x = 2;
> }
> }
>
> If two separate threads tried to initialize classes A and B and the
> same time, deadlock could occur.
>
> Here is the sequence of events:
>
> T1 executes steps 1 and 6 of 2.17.5 on class A. Class A is now marked
> as InProgress, and unlocked.
>
> T2 executes steps 1 and 6 of 2.17.5 on class B. Class B is now marked
> as InProgress, and unlocked.
>
> T1 executes step 8 of 2.17.5 on class A. This involves an
> active use of B,
> so T1 tries to initialize B.
>
> T1 executes step 1 on class B. Since B is marked as
> InProgress, T1 then
> executes step 2, by waiting on B.
>
> T2 executes step 8 of 2.17.5 on class B. This involves an
> active use of A,
> so T2 tries to initialize B.
>
> T2 executes step 1 on class A. Since A is marked as
> InProgress, T2 then
> executes step 2, by waiting on A.
>
> Deadlock.
>
> I've been able to get this behavior to occur very consistently by
> putting a two-way barrier in the static initializers for class A and
> B.
>
> Is this something we want to fix?
>
> Note that circular initialization does occur. From the 1.4.2
> b18 build:
> IC: Initialization circularity between
> com.sun.jndi.ldap.LdapPoolManager and com.sun.jndi.ldap.pool.Pool
> IC: Initialization circularity between
> sun.awt.datatransfer.DataTransferer and
> sun.awt.datatransfer.DataTransferer$CharsetComparator
> IC: Initialization circularity between
> sun.awt.datatransfer.DataTransferer and
> sun.awt.datatransfer.DataTransferer$DataFlavorComparator
> IC: Initialization circularity between
> sun.awt.datatransfer.DataTransferer$CharsetComparator and
> sun.awt.datatransfer.DataTransferer$DataFlavorComparator
> IC: Initialization circularity between
> sun.nio.cs.ThreadLocalCoders and sun.nio.cs.ThreadLocalCoders$1
> IC: Initialization circularity between
> sun.nio.cs.ThreadLocalCoders and sun.nio.cs.ThreadLocalCoders$2
> IC: Initialization circularity between
> sun.nio.cs.ThreadLocalCoders$1 and sun.nio.cs.ThreadLocalCoders$2
>
>
> -------------------------------
> JavaMemoryModel mailing list -
http://www.cs.umd.edu/~pugh/java/memoryModel
-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:43 EDT