groovy
  1. groovy
  2. GROOVY-2495

MissingMethodExceptions in highly concurrent environments

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 1.5.1
    • Fix Version/s: 1.5.2
    • Component/s: None
    • Labels:
      None
    • Environment:
      Quad-core Xeon under 64-bit Suse Linux 9 with kernel 2.6.5-7.282-smp. CPU is Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
    • Testcase included:
      yes
    • Number of attachments :
      0

      Description

      I believe I have uncovered a threading bug with Groovy 1.5.1 which I can reproduce. This bug is consistently reproducible under 1.5.1, however I cannot get it to happen under 1.0. I have also tried the 1.6 trunk builds and the bug happens there as well.

      We recently starting getting groovy.lang.MissingMethodException at various times in our development code. Our code runs 100's of thousands of groovy expressions in a highly concurrent environment. It wasn't happening at the exact same point each time, but it started when we updated our development hardware (much faster and more cores than before). I've managed to reproduce the bug in a simple class (see below), but even in this class the number of failures varies greatly between runs. For every run, I will get some number of exceptions that look like this:

      Exception in thread "Thread-27" groovy.lang.MissingMethodException: No signature of method: Util.between() is applicable for argument types: (null, java.lang.Integer, java.lang.Integer) values:

      {null, 3, 11}
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at RunnableTest.run(RunnableTest.java:17)
      at java.lang.Thread.run(Thread.java:619)

      Although the types in the parameter list will vary.

      I'm pretty sure this indicates a threading issue. While groovy script is run 5 million times, it only will fail a small handful of times. One thing that is interesting is that I can consistently get the exceptions when running under Linux (which is our production environment), but I never get them running under Windows. On both platforms I am using the latest version of Java6.

      Any help would be much appreciated. Thanks again,

      Chuck
      import groovy.lang.Binding;
      import groovy.lang.GroovyShell;
      import groovy.lang.Script;
      
      public class BugTest {
      
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  new Thread(new RunnableTest("return util.between(new Short((short)4), new Integer(3), new Integer(11))")).start();
                  new Thread(new RunnableTest("return util.between(new Integer(4), new Integer(3), new Integer(11))")).start();
                  new Thread(new RunnableTest("return util.between(null, new Integer(3), new Integer(11))")).start();
                  new Thread(new RunnableTest("return util.between('1', '3', '11')")).start();
              }
          }
      
          public static class RunnableTest implements Runnable {
              private Script _script;
                 
              public RunnableTest(String expression) {
                  _script = new GroovyShell().parse(expression);
                  Binding b = new Binding();
                  b.setVariable("util", Util.getInstance());
                  _script.setBinding(b);
              }
                 
              public void run() {
                  for (int i = 0; i < 500000; i++)
                      _script.run();
              }
          }
      
          public static class Util {
              private static Util _INSTANCE = new Util();
               
              public static Util getInstance() {
                  return _INSTANCE;
              }
         
              public boolean between(Object value, Object low, Object high) {
                  if (value == null || low == null || high == null)
                      return false;
         
                  if (value instanceof String) {
                      String val = (String)value;
                      return val.compareTo(low.toString()) >= 0 && val.compareTo(high.toString()) <= 0;
                  }
         
                  if (value instanceof Number && low instanceof Number && high instanceof Number) {
                      double val = ((Number)value).doubleValue();
                      double l = ((Number)low).doubleValue();
                      double h = ((Number)high).doubleValue();
         
                      return val >= l && val <= h;
                  }
         
                  return false;
              }
          }
      }


      Here is the output from the last time I ran it:

      Exception in thread "Thread-7" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (null, java.lang.Integer, java.lang.Integer) values: {null, 3, 11}

      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-11" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (null, java.lang.Integer, java.lang.Integer) values:

      {null, 3, 11}
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-15" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (null, java.lang.Integer, java.lang.Integer) values: {null, 3, 11}

      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-2" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (java.lang.Integer, java.lang.Integer, java.lang.Integer) values:

      {4, 3, 11}
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-19" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (null, java.lang.Integer, java.lang.Integer) values: {null, 3, 11}
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-18" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (java.lang.Integer, java.lang.Integer, java.lang.Integer) values: {4, 3, 11}

      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-1" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (java.lang.Short, java.lang.Integer, java.lang.Integer) values:

      {4, 3, 11}

      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)
      Exception in thread "Thread-39" groovy.lang.MissingMethodException: No signature of method: BugTest$Util.between() is applicable for argument types: (null, java.lang.Integer, java.lang.Integer) values:

      {null, 3, 11}

      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:54)
      at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:169)
      at Script1.run(Script1.groovy:1)
      at BugTest$RunnableTest.run(BugTest.java:28)
      at java.lang.Thread.run(Thread.java:619)

        Activity

        Hide
        Chuck May added a comment -

        I've been doing some more testing. If I split the Util function "between" into separate functions with different names and parameter types, then the code runs fine and there are no errors. The problem seems to come from having a single function that is called with different sets of classes as parameters. In the previous example, the parameters were all "Object" but it was called with different types (Integer, Short, String). By having all the functions have different names I never see the MissingMethodException

        Not sure what this means, but it may be helpful to you.

        import groovy.lang.Binding;
        import groovy.lang.GroovyShell;
        import groovy.lang.Script;
        
        public class BugTest2 {
        
            public static void main(String[] args) {
                for (int i = 0; i < 10; i++) {
                    new Thread(new RunnableTest("return util.between1(new Short((short)4), new Integer(3), new Integer(11))")).start();
                    new Thread(new RunnableTest("return util.between2(new Integer(4), new Integer(3), new Integer(11))")).start();
                    new Thread(new RunnableTest("return util.between2(null, new Integer(3), new Integer(11))")).start();
                    new Thread(new RunnableTest("return util.between3('1', '3', '11')")).start();
                }
            }
        
            public static class RunnableTest implements Runnable {
                private Script _script;
                public RunnableTest(String expression) {
                    _script = new GroovyShell().parse(expression);
                    Binding b = new Binding();
                    b.setVariable("util", Util.getInstance());
                    _script.setBinding(b);
                }
        
                public void run() {
                    for (int i = 0; i < 500000; i++)
                        _script.run();
                }
            }
        
            public static class Util {
                private static Util _INSTANCE = new Util();
        
                public static Util getInstance() {
                    return _INSTANCE;
                }
        
                public boolean between1(Short value, Integer low, Integer high) {
                    if (value == null || low == null || high == null)
                        return false;
        
                    double val = ((Number)value).doubleValue();
                    double l = ((Number)low).doubleValue();
                    double h = ((Number)high).doubleValue();
        
                    return val >= l && val <= h;
               }
        
                public boolean between2(Integer value, Integer low, Integer high) {
                    if (value == null || low == null || high == null)
                        return false;
        
                    double val = ((Number)value).doubleValue();
                    double l = ((Number)low).doubleValue();
                    double h = ((Number)high).doubleValue();
        
                    return val >= l && val <= h;
               }
        
               public boolean between3(String value, String low, String high) {
                    if (value == null || low == null || high == null)
                        return false;
        
                    String val = (String)value;
                    return val.compareTo(low.toString()) >= 0 && val.compareTo(high.toString()) <= 0;
                }
            }
        }
        Show
        Chuck May added a comment - I've been doing some more testing. If I split the Util function "between" into separate functions with different names and parameter types, then the code runs fine and there are no errors. The problem seems to come from having a single function that is called with different sets of classes as parameters. In the previous example, the parameters were all "Object" but it was called with different types (Integer, Short, String). By having all the functions have different names I never see the MissingMethodException Not sure what this means, but it may be helpful to you. import groovy.lang.Binding; import groovy.lang.GroovyShell; import groovy.lang.Script; public class BugTest2 { public static void main( String [] args) { for ( int i = 0; i < 10; i++) { new Thread ( new RunnableTest( " return util.between1( new Short (( short )4), new Integer (3), new Integer (11))" )).start(); new Thread ( new RunnableTest( " return util.between2( new Integer (4), new Integer (3), new Integer (11))" )).start(); new Thread ( new RunnableTest( " return util.between2( null , new Integer (3), new Integer (11))" )).start(); new Thread ( new RunnableTest( " return util.between3('1', '3', '11')" )).start(); } } public static class RunnableTest implements Runnable { private Script _script; public RunnableTest( String expression) { _script = new GroovyShell().parse(expression); Binding b = new Binding(); b.setVariable( "util" , Util.getInstance()); _script.setBinding(b); } public void run() { for ( int i = 0; i < 500000; i++) _script.run(); } } public static class Util { private static Util _INSTANCE = new Util(); public static Util getInstance() { return _INSTANCE; } public boolean between1( Short value, Integer low, Integer high) { if (value == null || low == null || high == null ) return false ; double val = (( Number )value).doubleValue(); double l = (( Number )low).doubleValue(); double h = (( Number )high).doubleValue(); return val >= l && val <= h; } public boolean between2( Integer value, Integer low, Integer high) { if (value == null || low == null || high == null ) return false ; double val = (( Number )value).doubleValue(); double l = (( Number )low).doubleValue(); double h = (( Number )high).doubleValue(); return val >= l && val <= h; } public boolean between3( String value, String low, String high) { if (value == null || low == null || high == null ) return false ; String val = ( String )value; return val.compareTo(low.toString()) >= 0 && val.compareTo(high.toString()) <= 0; } } }
        Hide
        Guillaume Laforge added a comment -

        Should be fixed after the merges done on the 1.5 branch.

        Show
        Guillaume Laforge added a comment - Should be fixed after the merges done on the 1.5 branch.

          People

          • Assignee:
            blackdrag blackdrag
            Reporter:
            Chuck May
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: