JRuby (please use github issues at http://bugs.jruby.org)
  1. JRuby (please use github issues at http://bugs.jruby.org)
  2. JRUBY-6836

Strange interactions between a break statement and ensure blocks

    Details

    • Type: Bug Bug
    • Status: Resolved Resolved
    • Priority: Blocker Blocker
    • Resolution: Fixed
    • Affects Version/s: JRuby 1.7.0.pre2
    • Fix Version/s: JRuby 1.7.0.RC1
    • Component/s: None
    • Labels:
      None
    • Environment:
      64-bit Debian 6.0.3 running JDK 1.7.0_02
    • Number of attachments :
      1

      Description

      I've noticed a bit of strange behaviour in the attached test program. I have:

      • A main function with a top-level begin/ensure block, where the ensure block is supposed to perform some sort of cleanup at program exit.
      • A 'while true' loop that runs a bit of code in a loop. That code is expected to throw an exception for the first few iterations, and the loop continues until the exceptions stop (think of polling a web server until it starts up)
      • When the exceptions stop, the loop is terminated with a 'break'

      What I'm seeing: the code immediately following this loop never runs. We jump straight to the 'ensure' block and the program terminates. What I see:

      $ /usr/local/jdk1.7.0_02/bin/java -cp jruby-complete-1.7.0.preview2.jar org.jruby.Main test.rb 
      Around we go
      Around we go
      Around we go
      Ensure block
      
      

      the statement 'puts "Why doesn't this get printed?"' never seems to fire.

      I've tested against the following JVMs:

      java version "1.6.0_30"
      Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
      Java HotSpot(TM) 64-Bit Server VM (build 20.5-b03, mixed mode)
      
      java version "1.7.0_02"
      Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 22.0-b10, mixed mode)
      

      and I see the same issue with jruby-complete-1.6.7.2.jar as with jruby-complete-1.7.0.preview2.jar.

      Please let me know if I can do any debugging from this side.

      Cheers,

      Mark

      1. test.rb
        0.3 kB
        Mark Triggs

        Activity

        Hide
        Charles Oliver Nutter added a comment -

        Looks like it's a bug in the compiler's handling of break in this particular case (old interpreter and IR interpreter are ok):

        system ~/projects/jruby $ jruby tmp/test.rb
        Around we go
        Around we go
        Around we go
        Ensure block
        
        
        system ~/projects/jruby $ jruby -X-C tmp/test.rb
        Around we go
        Around we go
        Around we go
        Why doesn't this get printed?
        Ensure block
        
        system ~/projects/jruby $ jruby -X-CIR tmp/test.rb
        Around we go
        Around we go
        Around we go
        Why doesn't this get printed?
        Ensure block
        
        Show
        Charles Oliver Nutter added a comment - Looks like it's a bug in the compiler's handling of break in this particular case (old interpreter and IR interpreter are ok): system ~/projects/jruby $ jruby tmp/test.rb Around we go Around we go Around we go Ensure block system ~/projects/jruby $ jruby -X-C tmp/test.rb Around we go Around we go Around we go Why doesn't this get printed? Ensure block system ~/projects/jruby $ jruby -X-CIR tmp/test.rb Around we go Around we go Around we go Why doesn't this get printed? Ensure block
        Hide
        Charles Oliver Nutter added a comment -

        Slightly smaller reproduction. The outer ensure is necessary to trigger this.

        system ~/projects/jruby $ cat tmp/test.rb
        def main
          while true
            begin
              break
            rescue
            end
          end
          puts "Why doesn't this get printed?"
        ensure
          puts 'ensure'
        end
        

        Note that while simplified, this is still begin/ensure containing while containing begin/rescue. The outer ensure can't be a rescue, but the inner rescue can be an ensure with the same effect.

        Show
        Charles Oliver Nutter added a comment - Slightly smaller reproduction. The outer ensure is necessary to trigger this. system ~/projects/jruby $ cat tmp/test.rb def main while true begin break rescue end end puts "Why doesn't this get printed?" ensure puts 'ensure' end Note that while simplified, this is still begin/ensure containing while containing begin/rescue. The outer ensure can't be a rescue, but the inner rescue can be an ensure with the same effect.
        Hide
        Charles Oliver Nutter added a comment -
        commit a6d25cae134d847c0152c52181592cd469461aad
        Author: Charles Oliver Nutter <headius@headius.com>
        Date:   Tue Aug 21 21:31:17 2012 -0500
        
            Fix JRUBY-6836
            
            Strange interactions between a break statement and ensure blocks
            
            The current compiler does an "inspection" pass over the AST to
            see whether certain optimizations are possible. For the most part,
            this inspection just sets a few bits on the inspector that are
            used to alter compilation, but there are a few cases like non-
            local flow control (e.g. a while loop with a break inside a
            closure or rescue/ensure) where we actually alter the AST to
            indicate there's something special. The presence of certain AST
            nodes disables all optimizations, largely because those nodes are
            difficult or impossible to support properly without JVM gymnastics
            that make our usual set of optimizations impossible. ensure is one
            such case, and the inspector disables all optimizations when an
            ensure is encountered. However, it should still run the inspection
            of all child nodes, since as mentioned above there are state
            changes on the AST itself that still need to happen in order for
            those nodes to compile properly.
            
            I audited all places in ASTInspector where we do a hard disable()
            call and made sure those locations still proceed to inspect the
            downstream AST nodes, which fixes this bug and should prevent
            other cases from popping up.
        
        :100644 100644 70980de... 278c7ff... M	spec/compiler/general_spec.rb
        :100644 100644 e633b01... 8bc9931... M	src/org/jruby/compiler/ASTInspector.java
        
        Show
        Charles Oliver Nutter added a comment - commit a6d25cae134d847c0152c52181592cd469461aad Author: Charles Oliver Nutter <headius@headius.com> Date: Tue Aug 21 21:31:17 2012 -0500 Fix JRUBY-6836 Strange interactions between a break statement and ensure blocks The current compiler does an "inspection" pass over the AST to see whether certain optimizations are possible. For the most part, this inspection just sets a few bits on the inspector that are used to alter compilation, but there are a few cases like non- local flow control (e.g. a while loop with a break inside a closure or rescue/ensure) where we actually alter the AST to indicate there's something special. The presence of certain AST nodes disables all optimizations, largely because those nodes are difficult or impossible to support properly without JVM gymnastics that make our usual set of optimizations impossible. ensure is one such case, and the inspector disables all optimizations when an ensure is encountered. However, it should still run the inspection of all child nodes, since as mentioned above there are state changes on the AST itself that still need to happen in order for those nodes to compile properly. I audited all places in ASTInspector where we do a hard disable() call and made sure those locations still proceed to inspect the downstream AST nodes, which fixes this bug and should prevent other cases from popping up. :100644 100644 70980de... 278c7ff... M spec/compiler/general_spec.rb :100644 100644 e633b01... 8bc9931... M src/org/jruby/compiler/ASTInspector.java
        Hide
        Mark Triggs added a comment -

        Many thanks for the speedy fix, Charles

        Show
        Mark Triggs added a comment - Many thanks for the speedy fix, Charles

          People

          • Assignee:
            Charles Oliver Nutter
            Reporter:
            Mark Triggs
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: