JDK-6963811 : Deadlock-prone locking changes in Introspector
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 6u21
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2010-06-24
  • Updated: 2013-09-12
  • Resolved: 2010-07-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other Other JDK 6 JDK 7
5.0u36Fixed 5.0u38Fixed 6u23Fixed 7 b102Fixed
Related Reports
Relates :  
Relates :  
Description
Locking model of Introspector changed by fix of
bug 5102804. The code

(new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();

is called under BEANINFO_CACHE lock by now, see getBeanInfo(Class) method.
This code can invoke 3rd-party code through findExplicitBeanInfo().
It is deadlock-prone to call 3rd-party code under some lock
and it should be avoided where it is possible.

See the following test-case. It shows how easily the mentioned
changes can result in a deadlock.

-------------------------------------------------

package problemofintrospector;

import java.awt.Point;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.SimpleBeanInfo;

public class TestCase {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                try {
                    // Get some BeanInfo in one thread
                    Introspector.getBeanInfo(CustomizedPoint.class, Object.class);
                } catch (IntrospectionException iex) {}
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                try {
                    // Get some BeanInfo in another thread
                    Introspector.getBeanInfo(Point.class);
                } catch (IntrospectionException iex) {}
            }
        });
        t1.start();
        Thread.sleep(500); // increase the chance of the deadlock
        t2.start();
    }

    public static class CustomizedPoint extends Point {
    }

    public static class CustomizedPointBeanInfo extends SimpleBeanInfo {
        private BeanInfo superInfo;

        public CustomizedPointBeanInfo() {
            try {
                Thread.sleep(1000); // increase the chance of the deadlock
                superInfo = Introspector.getBeanInfo(Point.class);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        // Real-life BeanInfo would contain some additional
        // methods overriden and some work delegated to superInfo
        // (the corresponding code is skipped because it is not
        // necessary to reproduce the deadlock).

    }

}

-------------------------------------------------

Note that the test-case doesn't run into deadlock on older JDK builds
(pre JDK 6 update 21 build 02), i.e., it is a regression. Also note
that this test-case is not an artifical one. It is based on a deadlock
that one of our users faces regularly while using NetBeans GUI Builder.

Comments
WORK AROUND Use Introspector on a single thread.
24-06-2010

EVALUATION It was introduced by the fix of the 5102804 CR or the 6380849 CR. The reason is that the BeanInfo finder uses own mutex. We should either use the Introspector's mutex for the finder or synchronize mutable fields only.
24-06-2010

SUGGESTED FIX The fix of issue 5102804 uses BEANINFO_CACHE lock to synchronize beanInfoCache collection. You can either make this collection synchronized using Collections.synchronizedMap (and strip the synchronization in getBeanInfo(Class)) or leave the collection as it is and (at least) move invocation of (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); outside BEANINFO_CACHE lock, i.e., replace synchronized (BEANINFO_CACHE) { ... BeanInfo beanInfo = beanInfoCache.get(beanClass); if (beanInfo == null) { beanInfo = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); beanInfoCache.put(beanClass, beanInfo); } } by synchronized (BEANINFO_CACHE) { ... BeanInfo beanInfo = beanInfoCache.get(beanClass); } if (beanInfo == null) { beanInfo = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo(); synchronized (BEANINFO_CACHE) { beanInfoCache.put(beanClass, beanInfo); } }
24-06-2010