Skip to content

Commit 7f2c689

Browse files
Boereckdblock
authored andcommitted
Pulled varargs test out of the loop to avoid checking on each parameter
The Method#isVarargs method is now referenced statically to avoid costs of looking it up on each native method call with at least one parameter. This also can save a lot of memory, since on every Class#getMethod() call a new Method object is returned. Added null check uses the double-checked locking to speed up subsequent calls to the same function. Made Function$Handler$FunctionInfo immutable class to make double check locking work correctly. Added description of changes to CHANGES.md
1 parent 80d4c13 commit 7f2c689

3 files changed

Lines changed: 69 additions & 34 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Features
1818
* [#286](https://github.com/twall/jna/pull/286): Added in com.sun.jna.platform.win32.Kernel32: CreateRemoteThread, WritePocessMemory and ReadProcessMemory - [@sstokic-tgm](https://github.com/sstokic-tgm).
1919
* [#350](https://github.com/twall/jna/pull/350): Added `jnacontrib.x11.api.X.Window.getSubwindows` - [@rm5248](https://github.com/rm5248).
2020
* Improved `contrib/msoffice` sample - [@wolftobias](https://github.com/wolftobias).
21+
* [#352](https://github.com/twall/jna/pull/352): Performance improvements due to reduced locking in `com.sun.jna.Library$Handler` and fewer vararg checks in `com.sun.jna.Function` - [@Boereck](https://github.com/Boereck).
2122

2223
Bug Fixes
2324
---------

src/com/sun/jna/Function.java

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,10 @@ public Object invoke(Class returnType, Object[] inArgs, Map options) {
288288
Method invokingMethod = (Method)options.get(OPTION_INVOKING_METHOD);
289289
Class[] paramTypes = invokingMethod != null ? invokingMethod.getParameterTypes() : null;
290290
boolean allowObjects = Boolean.TRUE.equals(options.get(Library.OPTION_ALLOW_OBJECTS));
291+
boolean isVarArgs = args.length > 0 && invokingMethod != null ? isVarArgs(invokingMethod) : false;
291292
for (int i=0; i < args.length; i++) {
292293
Class paramType = invokingMethod != null
293-
? (isVarArgs(invokingMethod) && i >= paramTypes.length-1
294+
? (isVarArgs && i >= paramTypes.length-1
294295
? paramTypes[paramTypes.length-1].getComponentType()
295296
: paramTypes[i])
296297
: null;
@@ -776,21 +777,34 @@ static Object[] concatenateVarArgs(Object[] inArgs) {
776777
return inArgs;
777778
}
778779

779-
/** Varargs are only supported on 1.5+. */
780-
static boolean isVarArgs(Method m) {
780+
/**
781+
* Reference to Method.isVarArgs
782+
*/
783+
private static Method isVarArgsMethod = getIsVarArgsMethod();
784+
785+
/**
786+
* If possible returns the Method.isVarArgs method via reflection
787+
* @return Method.isVarArgs method
788+
*/
789+
private static Method getIsVarArgsMethod() {
781790
try {
782-
Method v = m.getClass().getMethod("isVarArgs", new Class[0]);
783-
return Boolean.TRUE.equals(v.invoke(m, new Object[0]));
784-
}
785-
catch (SecurityException e) {
791+
return Method.class.getMethod("isVarArgs", new Class[0]);
792+
} catch (SecurityException e) {
793+
return null;
794+
} catch (NoSuchMethodException e) {
795+
return null;
786796
}
787-
catch (NoSuchMethodException e) {
788-
}
789-
catch (IllegalArgumentException e) {
790-
}
791-
catch (IllegalAccessException e) {
792-
}
793-
catch (InvocationTargetException e) {
797+
}
798+
799+
/** Varargs are only supported on 1.5+. */
800+
static boolean isVarArgs(Method m) {
801+
if(isVarArgsMethod != null) {
802+
try {
803+
return Boolean.TRUE.equals(isVarArgsMethod.invoke(m, new Object[0]));
804+
} catch (IllegalArgumentException e) {
805+
} catch (IllegalAccessException e) {
806+
} catch (InvocationTargetException e) {
807+
}
794808
}
795809
return false;
796810
}

src/com/sun/jna/Library.java

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,25 @@ public Class getInterfaceClass() {
160160
return interfaceClass;
161161
}
162162

163-
private static class FunctionInfo {
164-
InvocationHandler handler;
165-
Function function;
166-
boolean isVarArgs;
167-
Map options;
163+
/**
164+
* FunctionInfo has to be immutable to to make the object visible
165+
* to other threads fully initialized. This is a prerequisite for
166+
* using the class in the double checked locking scenario of {@link Handler#invoke(Object, Method, Object[])}
167+
*/
168+
private static final class FunctionInfo {
169+
170+
FunctionInfo(InvocationHandler handler, Function function, boolean isVarArgs, Map options) {
171+
super();
172+
this.handler = handler;
173+
this.function = function;
174+
this.isVarArgs = isVarArgs;
175+
this.options = options;
176+
}
177+
178+
final InvocationHandler handler;
179+
final Function function;
180+
final boolean isVarArgs;
181+
final Map options;
168182
}
169183

170184
public Object invoke(Object proxy, Method method, Object[] inArgs)
@@ -185,22 +199,28 @@ else if (OBJECT_EQUALS.equals(method)) {
185199
return Boolean.FALSE;
186200
}
187201

188-
FunctionInfo f = null;
189-
synchronized(functions) {
190-
f = (FunctionInfo)functions.get(method);
191-
if (f == null) {
192-
f = new FunctionInfo();
193-
f.isVarArgs = Function.isVarArgs(method);
194-
if (invocationMapper != null) {
195-
f.handler = invocationMapper.getInvocationHandler(nativeLibrary, method);
196-
}
197-
if (f.handler == null) {
198-
// Find the function to invoke
199-
f.function = nativeLibrary.getFunction(method.getName(), method);
200-
f.options = new HashMap(this.options);
201-
f.options.put(Function.OPTION_INVOKING_METHOD, method);
202+
// Using the double-checked locking pattern to speed up function calls
203+
FunctionInfo f = (FunctionInfo)functions.get(method);
204+
if(f == null) {
205+
synchronized(functions) {
206+
f = (FunctionInfo)functions.get(method);
207+
if (f == null) {
208+
boolean isVarArgs = Function.isVarArgs(method);
209+
InvocationHandler handler = null;
210+
if (invocationMapper != null) {
211+
handler = invocationMapper.getInvocationHandler(nativeLibrary, method);
212+
}
213+
Function function = null;
214+
Map options = null;
215+
if (handler == null) {
216+
// Find the function to invoke
217+
function = nativeLibrary.getFunction(method.getName(), method);
218+
options = new HashMap(this.options);
219+
options.put(Function.OPTION_INVOKING_METHOD, method);
220+
}
221+
f = new FunctionInfo(handler, function, isVarArgs, options);
222+
functions.put(method, f);
202223
}
203-
functions.put(method, f);
204224
}
205225
}
206226
if (f.isVarArgs) {

0 commit comments

Comments
 (0)