Details

    • Type: Bug Bug
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: JRuby 1.6.4
    • Fix Version/s: JRuby 1.7.0.pre1
    • Component/s: Java Integration
    • Labels:
      None
    • Environment:
      JRuby 1.6.4,
      java version "1.6.0_26"
      Java(TM) SE Runtime Environment (build 1.6.0_26-b03-384-10M3425)
      Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-384, mixed mode)
    • Testcase included:
      yes
    • Patch Submitted:
      Yes
    • Number of attachments :
      2

      Description

      Discussed this on twitter with @headius and @technomancy a bit. Clojure's STM uses exceptions for retries within a transaction (clojure.lang.LockingTransaction$RetryEx). When these exceptions pass through JRuby, they're wrapped, causing the STM transaction logic higher up the stack to fail since it can't catch the retry exceptions any more.

      So, I'm partly just reporting the issue, but I was able to create a fairly simple test case (no Clojure or threads) and very naive solution. It's just a Set of unrescuable exception types in JavaSupport, extendable by Ruby code. I'm sure this is wrong in many ways since I've never looked at the JRuby source before, but it was an interesting exercise

      Example and patch attached.

      1. unrescuable.patch
        2 kB
        Dave Ray
      2. unrescuable.tgz
        1 kB
        Dave Ray

        Activity

        Hide
        Charles Oliver Nutter added a comment -

        I think the simplest answer might be to have a JRuby ext that wraps the Clojure STM and allows the exceptions to propagate out. Current Java integration in JRuby can't support propagating the actual exception (because people have dependencies on the wrapper) and there are some tricky issues to move that direction in the future.

        Show
        Charles Oliver Nutter added a comment - I think the simplest answer might be to have a JRuby ext that wraps the Clojure STM and allows the exceptions to propagate out. Current Java integration in JRuby can't support propagating the actual exception (because people have dependencies on the wrapper) and there are some tricky issues to move that direction in the future.
        Hide
        Charles Oliver Nutter added a comment -

        I am working on an experimental patch that removes the wrapping. After spiking a few ideas, I think I've come up with one that will work.

        We will allow the real exception to propagate. We will no longer wrap it. Rescue clauses already can rescue the real exception class, but in the future that will be strongly recommended.

        However, to make sure existing code continues to work, rescuing NativeException will still rescue Java exceptions propagating through JRuby. We wil lazily wrap them in a NativeException object, so it will work as before.

        There are some grey areas, of course:

        • Rescuing Object or Exception will, as they do now, rescue Java exceptions. However, they'll get the real exception rather than the NativeException wrapper. This could affect code that does a blanket rescue and then expects to get a NativeException object.
        • Re-raising a NativeException should probably actually raise the wrapped Throwable. Raising the wrapper would cause upstream code to get unpredictable results.
        • Entry points into JRuby currently do not "throws" anything. We may need to add "throws Throwable" entry points to allow callers to catch any exception type. This is entirely to appease Java's checked exceptions.

        Once I have a prototype that does all this, I'll post a patch.

        Show
        Charles Oliver Nutter added a comment - I am working on an experimental patch that removes the wrapping. After spiking a few ideas, I think I've come up with one that will work. We will allow the real exception to propagate. We will no longer wrap it. Rescue clauses already can rescue the real exception class, but in the future that will be strongly recommended. However, to make sure existing code continues to work, rescuing NativeException will still rescue Java exceptions propagating through JRuby. We wil lazily wrap them in a NativeException object, so it will work as before. There are some grey areas, of course: Rescuing Object or Exception will, as they do now, rescue Java exceptions. However, they'll get the real exception rather than the NativeException wrapper. This could affect code that does a blanket rescue and then expects to get a NativeException object. Re-raising a NativeException should probably actually raise the wrapped Throwable. Raising the wrapper would cause upstream code to get unpredictable results. Entry points into JRuby currently do not "throws" anything. We may need to add "throws Throwable" entry points to allow callers to catch any exception type. This is entirely to appease Java's checked exceptions. Once I have a prototype that does all this, I'll post a patch.
        Hide
        Charles Oliver Nutter added a comment -

        I have landed a change in 3391966 that fixes this. There's some additional work to be done, but the basics are there.

        With the change, Java exceptions always propagate as themselves, even propagating all the way out of JRuby. The NativeException wrapper is mostly eliminated.

        commit 33919667a45607aa3e7a23765e396d2dd940d643
        Author: Charles Oliver Nutter <headius@headius.com>
        Date:   Mon Apr 9 10:40:30 2012 -0500
        
            Propagate Java exceptions without wrapping in NativeException.
            
            Fix for JRUBY-6149 and others.
            
            Previous versions of JRuby wrapped Java exceptions raised from
            calling Java methods inside NativeException. This provided a way
            to rescue them, but also caused them to remain wrapped when they
            eventually leave Ruby-space. This in turn prevented some libraries
            that depend on the *original* exception propagating from working
            correctly, like Clojure wanting IllegalStateException to know an
            STM commit should be run again.
            
            This change removes NativeException from propagating, and instead
            actually propagates the original Java exception in JI-wrapped
            form. Rescue NativeException still works, but it rescues the real
            Java exception instead of a NativeException instance. This may
            need to be modified to wrap in NativeException.
        
        Show
        Charles Oliver Nutter added a comment - I have landed a change in 3391966 that fixes this. There's some additional work to be done, but the basics are there. With the change, Java exceptions always propagate as themselves, even propagating all the way out of JRuby. The NativeException wrapper is mostly eliminated. commit 33919667a45607aa3e7a23765e396d2dd940d643 Author: Charles Oliver Nutter <headius@headius.com> Date: Mon Apr 9 10:40:30 2012 -0500 Propagate Java exceptions without wrapping in NativeException. Fix for JRUBY-6149 and others. Previous versions of JRuby wrapped Java exceptions raised from calling Java methods inside NativeException. This provided a way to rescue them, but also caused them to remain wrapped when they eventually leave Ruby-space. This in turn prevented some libraries that depend on the *original* exception propagating from working correctly, like Clojure wanting IllegalStateException to know an STM commit should be run again. This change removes NativeException from propagating, and instead actually propagates the original Java exception in JI-wrapped form. Rescue NativeException still works, but it rescues the real Java exception instead of a NativeException instance. This may need to be modified to wrap in NativeException.

          People

          • Assignee:
            Charles Oliver Nutter
            Reporter:
            Dave Ray
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: