PicoContainer
  1. PicoContainer
  2. PICO-199

Picocontainer 1.0 causes deadlock with SynchronizedComponentAdapters

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      2

      Description

      We've uncovered a deadlock in Picocontainer 1.0. Attached is a test case that demonstrates it.

      If your components only have dependencies on interfaces, who's implementations are registered with their interfaces as the key, then you will never get this deadlock.

      The deadlock will only manifest if you register things in a "wacky" way (e.g. as in the test case, or by having components with multiple ctors some of which have unsatisfiable dependencies). In this case, the container will look at all ComponentAdapters to see if they can provide the implementation class. Two SynchronisedComponentAdapters doing this at the same time will result in a deadlock.

      The fix is to not synchronise on the immutable state in SynchronisedComponentAdapter, see attached.

        Activity

        Manish Shah made changes -
        Field Original Value New Value
        Attachment PicoContainerDeadlockTest.java [ 12596 ]
        Hide
        Manish Shah added a comment -

        patch

        Show
        Manish Shah added a comment - patch
        Manish Shah made changes -
        Attachment SynchronizedComponentAdapter.java [ 12597 ]
        Jörg Schaible made changes -
        Assignee Joerg Schaible [ joehni ]
        Hide
        Jörg Schaible added a comment -

        Sorry, I cannot reproduce this. Your test case works perfectly in the head revision and the SynchedCA has not changed since pico 1.0. I've tested this on my linux box with Sun JDK 1.4.2 and Blackdown JDK 1.4.1.

        I've added your test case as java/picocontainer/src/test/org/picocontainer/defaults/issues/Issue0199TestCase.java

        see also unit test for
        java/picocontainer/src/test/org/picocontainer/defaults/SynchronizedComponentAdapterTestCase.java

        – Jörg

        Show
        Jörg Schaible added a comment - Sorry, I cannot reproduce this. Your test case works perfectly in the head revision and the SynchedCA has not changed since pico 1.0. I've tested this on my linux box with Sun JDK 1.4.2 and Blackdown JDK 1.4.1. I've added your test case as java/picocontainer/src/test/org/picocontainer/defaults/issues/Issue0199TestCase.java see also unit test for java/picocontainer/src/test/org/picocontainer/defaults/SynchronizedComponentAdapterTestCase.java – Jörg
        Hide
        Manish Shah added a comment -

        Hi Jörg,

        The test fails on my box against the head revision. I'm running Windows XP and Sun JDK 1.4.1.

        Try increasing the thread count and wait time if running on Linux.

        By the way, I'm not sure this test should be added to the test suite. I only wrote it to demonstrate the problem, but as it is timing dependent it may produce intermittent failures on different OS/JDK versions.

        Cheers,
        Manish

        Show
        Manish Shah added a comment - Hi Jörg, The test fails on my box against the head revision. I'm running Windows XP and Sun JDK 1.4.1. Try increasing the thread count and wait time if running on Linux. By the way, I'm not sure this test should be added to the test suite. I only wrote it to demonstrate the problem, but as it is timing dependent it may produce intermittent failures on different OS/JDK versions. Cheers, Manish
        Hide
        Jörg Schaible added a comment -

        Hi Manish,

        I increased on my box the thread count to 40 and it runs pertectly. OTOH I am also not sure, why it sould fail, since we have similar tests in the SyncedCA (see current HEAD, I've collected some others). Additionally I do not feel comfortable with your modification for SyncCA, since you don't know, what an adapter might really do in getComponentImplementation.

        We lifted the restriction for a concrete class at registration time and the adapter might do an action to determin its implementation class. Think of EJB or JMX.

        – Jörg

        Show
        Jörg Schaible added a comment - Hi Manish, I increased on my box the thread count to 40 and it runs pertectly. OTOH I am also not sure, why it sould fail, since we have similar tests in the SyncedCA (see current HEAD, I've collected some others). Additionally I do not feel comfortable with your modification for SyncCA, since you don't know, what an adapter might really do in getComponentImplementation. We lifted the restriction for a concrete class at registration time and the adapter might do an action to determin its implementation class. Think of EJB or JMX. – Jörg
        Hide
        Paul Hammant added a comment -

        0199 testcase is failing for me on Windows.

        java version "1.4.2_04"
        Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05)
        Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode)

        Am going to apply remainder of patch

        • Paul
        Show
        Paul Hammant added a comment - 0199 testcase is failing for me on Windows. java version "1.4.2_04" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_04-b05) Java HotSpot(TM) Client VM (build 1.4.2_04-b05, mixed mode) Am going to apply remainder of patch Paul
        Hide
        Jörg Schaible added a comment -

        Patch applied by Paul.

        Show
        Jörg Schaible added a comment - Patch applied by Paul.
        Jörg Schaible made changes -
        Status Open [ 1 ] Closed [ 6 ]
        Resolution Fixed [ 1 ]
        Hide
        Manish Shah added a comment -

        Jörg,

        Interesting that the deadlock does not occur on Linux. Perhaps the processor is not context switching? Try putting a sleep at the start of the runner thread, or in the ctor of classes B and C.

        FYI, attached is the stack-trace of a thread in deadlock (if we revert back to the SyncCA before the patch). I've had a look at the existing tests, and none cover this scenario.

        A thread is executing inside SyncCA.getComponentInstance. As part of its ecxecution, it tries to call CA.getComponentImplementation on every other registered instance of CA. Another thread doing this same thing inside a different instance of SyncCA will cause the deadlock.

        I'm not sure what the implications of not syncing on getComponentImplementation are. Actually, I have been wondering why we don't make Cyclic Dependency detection thread-safe. Then we wouldn't need SyncCA and race-conditions would be reduced.

        Cheers,
        Manish

        Show
        Manish Shah added a comment - Jörg, Interesting that the deadlock does not occur on Linux. Perhaps the processor is not context switching? Try putting a sleep at the start of the runner thread, or in the ctor of classes B and C. FYI, attached is the stack-trace of a thread in deadlock (if we revert back to the SyncCA before the patch). I've had a look at the existing tests, and none cover this scenario. A thread is executing inside SyncCA.getComponentInstance. As part of its ecxecution, it tries to call CA.getComponentImplementation on every other registered instance of CA. Another thread doing this same thing inside a different instance of SyncCA will cause the deadlock. I'm not sure what the implications of not syncing on getComponentImplementation are. Actually, I have been wondering why we don't make Cyclic Dependency detection thread-safe. Then we wouldn't need SyncCA and race-conditions would be reduced. Cheers, Manish
        Hide
        Manish Shah added a comment -

        Deadlock occurs at:
        org.picocontainer.defaults.SynchronizedComponentAdapter.getComponentImplementation(SynchronizedComponentAdapter.java:32)
        org.picocontainer.defaults.DefaultPicoContainer.getComponentAdaptersOfType(DefaultPicoContainer.java:181)
        org.picocontainer.defaults.DefaultPicoContainer.getComponentAdapterOfType(DefaultPicoContainer.java:155)
        org.picocontainer.defaults.ComponentParameter.resolveAdapter(ComponentParameter.java:75)
        org.picocontainer.defaults.ConstructorInjectionComponentAdapter.getGreediestSatisfiableConstructor(ConstructorInjectionComponentAdapter.java:88)
        org.picocontainer.defaults.ConstructorInjectionComponentAdapter.instantiateComponent(ConstructorInjectionComponentAdapter.java:184)
        org.picocontainer.defaults.InstantiatingComponentAdapter.getComponentInstance(InstantiatingComponentAdapter.java:81)
        org.picocontainer.defaults.DecoratingComponentAdapter.getComponentInstance(DecoratingComponentAdapter.java:42)
        org.picocontainer.defaults.SynchronizedComponentAdapter.getComponentInstance(SynchronizedComponentAdapter.java:37)
        org.picocontainer.defaults.DefaultPicoContainer.getComponentInstance(DefaultPicoContainer.java:324)
        org.picocontainer.defaults.issues.Issue0199TestCase$Runner.run(Issue0199TestCase.java:72)

        Show
        Manish Shah added a comment - Deadlock occurs at: org.picocontainer.defaults.SynchronizedComponentAdapter.getComponentImplementation(SynchronizedComponentAdapter.java:32) org.picocontainer.defaults.DefaultPicoContainer.getComponentAdaptersOfType(DefaultPicoContainer.java:181) org.picocontainer.defaults.DefaultPicoContainer.getComponentAdapterOfType(DefaultPicoContainer.java:155) org.picocontainer.defaults.ComponentParameter.resolveAdapter(ComponentParameter.java:75) org.picocontainer.defaults.ConstructorInjectionComponentAdapter.getGreediestSatisfiableConstructor(ConstructorInjectionComponentAdapter.java:88) org.picocontainer.defaults.ConstructorInjectionComponentAdapter.instantiateComponent(ConstructorInjectionComponentAdapter.java:184) org.picocontainer.defaults.InstantiatingComponentAdapter.getComponentInstance(InstantiatingComponentAdapter.java:81) org.picocontainer.defaults.DecoratingComponentAdapter.getComponentInstance(DecoratingComponentAdapter.java:42) org.picocontainer.defaults.SynchronizedComponentAdapter.getComponentInstance(SynchronizedComponentAdapter.java:37) org.picocontainer.defaults.DefaultPicoContainer.getComponentInstance(DefaultPicoContainer.java:324) org.picocontainer.defaults.issues.Issue0199TestCase$Runner.run(Issue0199TestCase.java:72)
        Hide
        Aslak Hellesøy added a comment -

        Do we need to reopen this issue?

        Show
        Aslak Hellesøy added a comment - Do we need to reopen this issue?
        Hide
        Jörg Schaible added a comment -

        Not without evidence. Cycle test is thread-safe now anyway.

        Show
        Jörg Schaible added a comment - Not without evidence. Cycle test is thread-safe now anyway.

          People

          • Assignee:
            Jörg Schaible
            Reporter:
            Manish Shah
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: