History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: OGNL-141
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Critical Critical
Assignee: Jesse Kuhnert
Reporter: Matthew Short
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
OGNL

Very serious OgnlRuntime scalability and concurrency bug in versions greater than 2.6.10

Created: 16/Jan/08 01:35 PM   Updated: 18/Jan/08 12:54 PM
Component/s: Core Runtime
Affects Version/s: 2.6.11
Fix Version/s: 2.7.2

Flags: Important


 Description  « Hide
This relates to the OgnlRuntime.invokeMethod.

Version 2.6.11 introduced sychronization on the Method instance for any Class whose instances are being traversed. Method instances, like Class instances, are singletons within a classloader. Net result: no two threads can call method.invoke concurrently, even against two different target object instances. Scalability is completely hosed.

I saw that in the 2.6.10 there could be race conditions on the accessibility permissions of the methods, so it does make sense to synchronize here. But in most high concurrency applications, I'd trade the ability to access private methods for scalability any day.



   public static Object invokeMethod( Object target, Method method, Object[] argsArray ) throws InvocationTargetException, IllegalAccessException
   {
       Object result;
       boolean wasAccessible = true;

       if (securityManager != null) {
           try {
               securityManager.checkPermission(getPermission(method));
           } catch (SecurityException ex) {
               throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
           }
       }
       synchronized (method) {
           if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
               if (!(wasAccessible = ((AccessibleObject)method).isAccessible())) {
                   ((AccessibleObject)method).setAccessible(true);
               }
           }
           result = method.invoke( target, argsArray );
           if (!wasAccessible) {
               ((AccessibleObject)method).setAccessible(false);
           }
       }
       return result;
   }



 All   Comments   Change History      Sort Order:
Matthew Short - [16/Jan/08 01:55 PM ]
Ah, here are excerpts from the log file that shows this problem happening under a scalability test

A thread waiting to lock
daemon prio=1 tid=0x4e9bf820 nid=0x781 waiting for monitor entry [0x47f2e000..0x47f2fe30]
at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:511)
- waiting to lock <0x6f4bdc88> (a java.lang.reflect.Method)
at ognl.OgnlRuntime.getMethodValue(OgnlRuntime.java:931)
at ognl.ObjectPropertyAccessor.getPossibleProperty(ObjectPropertyAccessor.java:53)
at ognl.ObjectPropertyAccessor.getProperty(ObjectPropertyAccessor.java:121)
at com.opensymphony.xwork.util.OgnlValueStack$ObjectAccessor.getProperty(OgnlValueStack.java:57)


And the initial lock:

prio=1 tid=0x5084c860 nid=0x778 runnable [0x49389000..0x4938ae30]
at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:517)
- locked <0x6f4bdc88> (a java.lang.reflect.Method)
at ognl.OgnlRuntime.getMethodValue(OgnlRuntime.java:931)
at ognl.ObjectPropertyAccessor.getPossibleProperty(ObjectPropertyAccessor.java:53)
at ognl.ObjectPropertyAccessor.getProperty(ObjectPropertyAccessor.java:121)
at com.opensymphony.xwork.util.OgnlValueStack$ObjectAccessor.getProperty(OgnlValueStack.java:57)

Jesse Kuhnert - [18/Jan/08 12:54 PM ]
Made some changes to OgnlRuntime to cache knowledge of whether or not a method needs security access checks or synchronized access because of private fields..

So, as long as your method doesn't require mucking with accessible flags you should be able to execute it concurrently now. (the changes made also have the side effect of increasing performance of method invocations for OGNL as a whole )

Until the snapshot is released you'll only be able to find a jar of this at http://opencomponentry.com/repository/m2-snapshot-repo/.

2.7.2 should be getting released to the normal ibiblio repo in the next couple weeks.