functional detach switch from callback
Esse commit está contido em:
+2
-2
@@ -45,7 +45,7 @@
|
||||
<property name="jni.revision" value="0"/>
|
||||
<property name="jni.build" value="${build.number}"/>
|
||||
<property name="jni.version" value="${jni.major}.${jni.minor}.${jni.revision}"/>
|
||||
<property name="jni.md5" value="4c69bcf40b30785215211b5a5dad211e"/>
|
||||
<property name="jni.md5" value="fff3cb3f61b8e1c7748924c5654073ee"/>
|
||||
<property name="spec.title" value="Java Native Access (JNA)"/>
|
||||
<property name="spec.vendor" value="${vendor}"/>
|
||||
<property name="spec.version" value="${jna.major}"/>
|
||||
@@ -548,7 +548,7 @@
|
||||
<doctitle>JNA API Documentation</doctitle>
|
||||
<header>${header}</header>
|
||||
<bottom>${footer}</bottom>
|
||||
<link href="http://java.sun.com/j2se/1.4.2/docs/api/"/>
|
||||
<link href="http://download.oracle.com/javase/1.4.2/docs/api/"/>
|
||||
|
||||
<packageset dir="${src}" defaultexcludes="yes">
|
||||
<patternset>
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ OS=$(shell uname | sed -e 's/\(CYGWIN\|MINGW32\).*/win32/g' \
|
||||
-e 's/Linux.*/linux/g')
|
||||
|
||||
JNA_JNI_VERSION=3.3.0 # auto-generated by ant
|
||||
CHECKSUM=4c69bcf40b30785215211b5a5dad211e # auto-generated by ant
|
||||
CHECKSUM=fff3cb3f61b8e1c7748924c5654073ee # auto-generated by ant
|
||||
|
||||
JAVA_INCLUDES=-I"$(JAVA_HOME)/include" \
|
||||
-I"$(JAVA_HOME)/include/$(OS)"
|
||||
|
||||
+20
-9
@@ -250,8 +250,9 @@ callback_invoke(JNIEnv* env, callback *cb, ffi_cif* cif, void *resp, void **cbar
|
||||
// Avoid calling back to a GC'd object
|
||||
if ((*env)->IsSameObject(env, self, NULL)) {
|
||||
fprintf(stderr, "JNA: callback object has been garbage collected\n");
|
||||
if (cif->rtype->type != FFI_TYPE_VOID)
|
||||
if (cif->rtype->type != FFI_TYPE_VOID) {
|
||||
memset(resp, 0, cif->rtype->size);
|
||||
}
|
||||
}
|
||||
else if (cb->direct) {
|
||||
unsigned int i;
|
||||
@@ -395,24 +396,31 @@ callback_dispatch(ffi_cif* cif, void* resp, void** cbargs, void* user_data) {
|
||||
JavaVM* jvm = cb->vm;
|
||||
JNIEnv* env;
|
||||
int was_attached = (*jvm)->GetEnv(jvm, (void *)&env, JNI_VERSION_1_4) == JNI_OK;
|
||||
jboolean detach = !was_attached;
|
||||
|
||||
if (!was_attached) {
|
||||
int daemon = 0;
|
||||
int attach_status = 0;
|
||||
JavaVMAttachArgs args;
|
||||
jobject group = NULL;
|
||||
|
||||
args.version = JNI_VERSION_1_2;
|
||||
args.name = NULL;
|
||||
args.group = NULL;
|
||||
if (cb->behavior_flags & CB_HAS_INITIALIZER) {
|
||||
initializeThread(cb, &args);
|
||||
args.group = initializeThread(cb, &args);
|
||||
}
|
||||
daemon = cb->behavior_flags & CB_DAEMON;
|
||||
if ((daemon && ((*jvm)->AttachCurrentThreadAsDaemon(jvm, (void*)&env, &args) != JNI_OK))
|
||||
|| (!daemon && ((*jvm)->AttachCurrentThread(jvm, (void *)&env, &args) != JNI_OK))) {
|
||||
fprintf(stderr, "JNA: Can't attach to native thread for callback\n");
|
||||
if (cb->behavior_flags & CB_DAEMON) {
|
||||
attach_status = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void*)&env, &args);
|
||||
}
|
||||
else {
|
||||
attach_status = (*jvm)->AttachCurrentThread(jvm, (void *)&env, &args);
|
||||
}
|
||||
if (attach_status != JNI_OK) {
|
||||
fprintf(stderr, "JNA: Can't attach to native thread for callback: %d\n", attach_status);
|
||||
return;
|
||||
}
|
||||
if (args.group) {
|
||||
(*env)->DeleteGlobalRef(env, args.group);
|
||||
(*env)->DeleteWeakGlobalRef(env, args.group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,10 +431,13 @@ callback_dispatch(ffi_cif* cif, void* resp, void** cbargs, void* user_data) {
|
||||
}
|
||||
else {
|
||||
callback_invoke(env, cb, cif, resp, cbargs);
|
||||
// Must be invoked immediately after return to avoid anything
|
||||
// stepping on errno/GetLastError
|
||||
detach = detachThread();
|
||||
(*env)->PopLocalFrame(env, NULL);
|
||||
}
|
||||
|
||||
if (!was_attached && !(cb->behavior_flags & CB_NODETACH)) {
|
||||
if (!was_attached && detach) {
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
}
|
||||
}
|
||||
|
||||
+49
-14
@@ -1122,7 +1122,7 @@ JNIEXPORT jchar JNICALL Java_com_sun_jna_Native_getChar
|
||||
/*
|
||||
* Class: Native
|
||||
* Method: _getPointer
|
||||
* Signature: (J)LPointer;
|
||||
* Signature: (J)Lcom/sun/jna/Pointer;
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_com_sun_jna_Native__1getPointer
|
||||
(JNIEnv *env, jclass UNUSED(cls), jlong addr)
|
||||
@@ -1132,6 +1132,19 @@ JNIEXPORT jlong JNICALL Java_com_sun_jna_Native__1getPointer
|
||||
return A2L(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: Native
|
||||
* Method: getObject
|
||||
* Signature: (J)Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_sun_jna_Native_getObject
|
||||
(JNIEnv *env, jclass UNUSED(cls), jlong addr)
|
||||
{
|
||||
jobject obj = NULL;
|
||||
MEMCPY(&obj, L2A(addr), sizeof(obj));
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: com_sun_jna_Native
|
||||
* Method: _getDirectByteBuffer
|
||||
@@ -1345,6 +1358,17 @@ JNIEXPORT void JNICALL Java_com_sun_jna_Native_setString
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: Native
|
||||
* Method: setObject
|
||||
* Signature: (JLjava/lang/Object;Z)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_sun_jna_Native_setObject
|
||||
(JNIEnv *env, jclass UNUSED(cls), jlong addr, jobject value)
|
||||
{
|
||||
value = (*env)->NewLocalRef(env, value);
|
||||
MEMCPY(L2A(addr), &value, sizeof(jobject));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: Native
|
||||
@@ -1721,30 +1745,33 @@ getCallbackAddress(JNIEnv *env, jobject obj) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
jobject
|
||||
initializeThread(callback* cb, JavaVMAttachArgs* args) {
|
||||
JavaVM* jvm = cb->vm;
|
||||
JNIEnv* env;
|
||||
jobject cbobj;
|
||||
jobject group = NULL;
|
||||
|
||||
if ((*jvm)->AttachCurrentThread(jvm, (void *)&env, &args) != JNI_OK) {
|
||||
fprintf(stderr, "JNA: Can't attach to native thread for callback\n");
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
(*env)->PushLocalFrame(env, 16);
|
||||
|
||||
cbobj = (*env)->NewLocalRef(env, cb->object);
|
||||
if (!(*env)->IsSameObject(env, cbobj, NULL)) {
|
||||
jobject argsobj = newJavaStructure(env, args, classJavaVMAttachArgs, JNI_FALSE);
|
||||
(*env)->CallStaticVoidMethod(env, classCallbackReference,
|
||||
MID_CallbackReference_initializeThread,
|
||||
cbobj, argsobj);
|
||||
if (args->group != NULL) {
|
||||
args->group = (*env)->NewGlobalRef(env, args->group);
|
||||
{
|
||||
jobject cbobj = (*env)->NewLocalRef(env, cb->object);
|
||||
if (!(*env)->IsSameObject(env, cbobj, NULL)) {
|
||||
jobject argsobj = newJavaStructure(env, args, classJavaVMAttachArgs, JNI_FALSE);
|
||||
group = (*env)->CallStaticObjectMethod(env, classCallbackReference,
|
||||
MID_CallbackReference_initializeThread,
|
||||
cbobj, argsobj);
|
||||
if (group != NULL) {
|
||||
group = (*env)->NewWeakGlobalRef(env, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
(*env)->PopLocalFrame(env, NULL);
|
||||
(*jvm)->DetachCurrentThread(jvm);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
jclass
|
||||
@@ -2034,7 +2061,7 @@ Java_com_sun_jna_Native_initIDs(JNIEnv *env, jclass cls) {
|
||||
}
|
||||
else if (!(MID_CallbackReference_initializeThread
|
||||
= (*env)->GetStaticMethodID(env, classCallbackReference,
|
||||
"initializeThread", "(Lcom/sun/jna/Callback;Lcom/sun/jna/CallbackReference$JavaVMAttachArgs;)V"))) {
|
||||
"initializeThread", "(Lcom/sun/jna/Callback;Lcom/sun/jna/CallbackReference$JavaVMAttachArgs;)Ljava/lang/ThreadGroup;"))) {
|
||||
throwByName(env, EUnsatisfiedLink,
|
||||
"Can't obtain static method initializeThread from class com.sun.jna.CallbackReference");
|
||||
}
|
||||
@@ -3066,6 +3093,14 @@ Java_com_sun_jna_Native_initialize_1ffi_1type(JNIEnv *env, jclass UNUSED(cls), j
|
||||
return (jint)type->size;
|
||||
}
|
||||
|
||||
/** Returns whether the current thread should be detached before return to
|
||||
native code.
|
||||
*/
|
||||
int detachThread() {
|
||||
// Kind of a hack, use last error value rather than setting up our own TLS
|
||||
return GET_LAST_ERROR() == THREAD_DETACH;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+4
-1
@@ -87,6 +87,8 @@ enum {
|
||||
CB_DAEMON = com_sun_jna_Native_CB_DAEMON,
|
||||
CB_NODETACH = com_sun_jna_Native_CB_NODETACH,
|
||||
CB_HAS_INITIALIZER = com_sun_jna_Native_CB_HAS_INITIALIZER,
|
||||
THREAD_ATTACH = com_sun_jna_Native_THREAD_ATTACH,
|
||||
THREAD_DETACH = com_sun_jna_Native_THREAD_DETACH,
|
||||
};
|
||||
|
||||
typedef struct _callback {
|
||||
@@ -185,7 +187,8 @@ extern void writeStructure(JNIEnv*, jobject);
|
||||
extern jclass getNativeType(JNIEnv*, jclass);
|
||||
extern void toNative(JNIEnv*, jobject, void*, size_t, jboolean);
|
||||
extern jclass fromNative(JNIEnv*, jclass, ffi_type*, void*, jboolean);
|
||||
extern void initializeThread(callback*,JavaVMAttachArgs*);
|
||||
extern jobject initializeThread(callback*,JavaVMAttachArgs*);
|
||||
extern int detachThread();
|
||||
|
||||
/* Native memory fault protection */
|
||||
#ifdef HAVE_PROTECTION
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ _exc_handler(struct _EXCEPTION_RECORD* exception_record,
|
||||
#define PROTECTED_END(ONERR) } __except((PROTECT)?EXCEPTION_EXECUTE_HANDLER:EXCEPTION_CONTINUE_SEARCH) { ONERR; }
|
||||
#else
|
||||
#ifdef _WIN64
|
||||
#error "GCC does not implement SEh"
|
||||
#error "GCC does not implement SEH"
|
||||
#else
|
||||
#define SEH_TRY(ER) \
|
||||
__asm__ ("movl %%fs:0, %0" : "=r" ((ER).ex_reg.prev)); \
|
||||
|
||||
+15
-8
@@ -553,25 +553,32 @@ callVoidCallback(void (*func)(void)) {
|
||||
(*func)();
|
||||
}
|
||||
|
||||
static int repeat_count = 0;
|
||||
static int sleep_time = 0;
|
||||
typedef struct thread_data {
|
||||
int repeat_count;
|
||||
int sleep_time;
|
||||
void (*func)(void);
|
||||
} thread_data;
|
||||
static void* thread_function(void *arg) {
|
||||
void (*func)(void) = (void (*)(void))arg;
|
||||
// make a local copy
|
||||
thread_data td = *(thread_data*)arg;
|
||||
void (*func)(void) = td.func;
|
||||
int i;
|
||||
for (i=0;i < repeat_count;i++) {
|
||||
for (i=0;i < td.repeat_count;i++) {
|
||||
int status;
|
||||
func();
|
||||
usleep(sleep_time*1000);
|
||||
usleep(td.sleep_time*1000);
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static thread_data data;
|
||||
EXPORT void
|
||||
callVoidCallbackThreaded(void (*func)(void), int n, int ms) {
|
||||
pthread_t thread;
|
||||
repeat_count = n;
|
||||
sleep_time = ms;
|
||||
pthread_create(&thread, NULL, &thread_function, func);
|
||||
data.repeat_count = n;
|
||||
data.sleep_time = ms;
|
||||
data.func = func;
|
||||
pthread_create(&thread, NULL, &thread_function, &data);
|
||||
}
|
||||
|
||||
EXPORT int
|
||||
|
||||
@@ -45,23 +45,6 @@ class CallbackReference extends WeakReference {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setCallbackOption(Callback cb, int options) {
|
||||
Map map = callbackMap;
|
||||
synchronized(map) {
|
||||
CallbackReference ref = (CallbackReference)map.get(cb);
|
||||
if (ref != null) {
|
||||
ref.setCallbackOption(options);
|
||||
}
|
||||
else {
|
||||
Integer current = (Integer)pendingOptions.get(cb);
|
||||
if (current != null) {
|
||||
options |= current.intValue();
|
||||
}
|
||||
pendingOptions.put(cb, new Integer(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Set behavioral options for the given callback object. */
|
||||
static void setCallbackOptions(Callback cb, int options) {
|
||||
Map map = callbackMap;
|
||||
@@ -71,15 +54,32 @@ class CallbackReference extends WeakReference {
|
||||
ref.setCallbackOptions(options);
|
||||
}
|
||||
else {
|
||||
Integer old = (Integer)pendingOptions.get(cb);
|
||||
pendingOptions.put(cb, new Integer(options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int getCallbackOptions(Callback cb) {
|
||||
Map map = callbackMap;
|
||||
synchronized(map) {
|
||||
CallbackReference ref = (CallbackReference)map.get(cb);
|
||||
if (ref != null) {
|
||||
return ref.getCallbackOptions();
|
||||
}
|
||||
else {
|
||||
Integer old = (Integer)pendingOptions.get(cb);
|
||||
return old != null ? old.intValue() : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map initializers = new WeakHashMap();
|
||||
static void setCallbackThreadInitializer(Callback cb, CallbackThreadInitializer initializer) {
|
||||
synchronized(callbackMap) {
|
||||
initializers.put(cb, initializer);
|
||||
int options = getCallbackOptions(cb);
|
||||
setCallbackOptions(cb, options | Native.CB_HAS_INITIALIZER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,16 +105,13 @@ class CallbackReference extends WeakReference {
|
||||
}
|
||||
return super.getFieldTypeInfo(f);
|
||||
}
|
||||
protected Object readField(StructField f) {
|
||||
if (ThreadGroup.class.equals(f.type)) {
|
||||
return null;
|
||||
}
|
||||
return super.readField(f);
|
||||
}
|
||||
}
|
||||
/** Called from native code to initialize a callback thread. */
|
||||
private static void initializeThread(Callback cb, JavaVMAttachArgs args) {
|
||||
private static ThreadGroup initializeThread(Callback cb, JavaVMAttachArgs args) {
|
||||
CallbackThreadInitializer init = null;
|
||||
if (cb instanceof DefaultCallbackProxy) {
|
||||
cb = ((DefaultCallbackProxy)cb).getCallback();
|
||||
}
|
||||
synchronized(initializers) {
|
||||
init = (CallbackThreadInitializer)initializers.get(cb);
|
||||
}
|
||||
@@ -127,15 +124,23 @@ class CallbackReference extends WeakReference {
|
||||
if (group != null) {
|
||||
args.group = group;
|
||||
}
|
||||
int options = 0;
|
||||
int options = getCallbackOptions(cb);
|
||||
if (init.isDaemon(cb)) {
|
||||
options |= Native.CB_DAEMON;
|
||||
}
|
||||
if (!init.detach(cb)) {
|
||||
else {
|
||||
options &= ~Native.CB_DAEMON;
|
||||
}
|
||||
if (init.detach(cb)) {
|
||||
options &= ~Native.CB_NODETACH;
|
||||
}
|
||||
else {
|
||||
options |= Native.CB_NODETACH;
|
||||
}
|
||||
setCallbackOption(cb, options);
|
||||
setCallbackOptions(cb, options);
|
||||
args.write();
|
||||
}
|
||||
return args.group;
|
||||
}
|
||||
|
||||
/** Return a Callback associated with the given function pointer.
|
||||
@@ -371,10 +376,9 @@ class CallbackReference extends WeakReference {
|
||||
cbstruct.setInt(Pointer.SIZE, options);
|
||||
}
|
||||
|
||||
/** Set a single behavioral option flag for this callback. */
|
||||
private void setCallbackOption(int options) {
|
||||
options |= cbstruct.getInt(Pointer.SIZE);
|
||||
cbstruct.setInt(Pointer.SIZE, options);
|
||||
/** Returns the currently set behavioral options for this callback. */
|
||||
private int getCallbackOptions() {
|
||||
return cbstruct.getInt(Pointer.SIZE);
|
||||
}
|
||||
|
||||
/** Obtain a pointer to the native glue code for this callback. */
|
||||
@@ -479,6 +483,10 @@ class CallbackReference extends WeakReference {
|
||||
}
|
||||
}
|
||||
|
||||
public Callback getCallback() {
|
||||
return CallbackReference.this.getCallback();
|
||||
}
|
||||
|
||||
private Object invokeCallback(Object[] args) {
|
||||
Class[] paramTypes = callbackMethod.getParameterTypes();
|
||||
Object[] callbackArgs = new Object[args.length];
|
||||
@@ -498,7 +506,7 @@ class CallbackReference extends WeakReference {
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
Callback cb = getCallback();
|
||||
Callback cb = DefaultCallbackProxy.this.getCallback();
|
||||
if (cb != null) {
|
||||
try {
|
||||
result = convertResult(callbackMethod.invoke(cb, callbackArgs));
|
||||
@@ -555,7 +563,7 @@ class CallbackReference extends WeakReference {
|
||||
value = ((Pointer)value).getStringArray(0, dstType == WString[].class);
|
||||
}
|
||||
else if (Callback.class.isAssignableFrom(dstType)) {
|
||||
value = getCallback(dstType, (Pointer)value);
|
||||
value = CallbackReference.this.getCallback(dstType, (Pointer)value);
|
||||
}
|
||||
else if (Structure.class.isAssignableFrom(dstType)) {
|
||||
Structure s = Structure.newInstance(dstType);
|
||||
|
||||
@@ -45,4 +45,4 @@ public class CallbackThreadInitializer {
|
||||
callback exits, if the thread was not already attached to begin with.
|
||||
*/
|
||||
public boolean detach(Callback cb) { return detach; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,9 @@ public final class Native {
|
||||
private static final int TYPE_WCHAR_T = 2;
|
||||
private static final int TYPE_SIZE_T = 3;
|
||||
|
||||
private static final int THREAD_DETACH = -1;
|
||||
private static final int THREAD_ATTACH = -2;
|
||||
|
||||
static {
|
||||
loadNativeLibrary();
|
||||
POINTER_SIZE = sizeof(TYPE_VOIDP);
|
||||
@@ -1055,6 +1058,11 @@ public final class Native {
|
||||
CallbackReference.setCallbackOptions(cb, options);
|
||||
}
|
||||
|
||||
/** Get callback options. */
|
||||
public static int getCallbackOptions(Callback cb) {
|
||||
return CallbackReference.getCallbackOptions(cb);
|
||||
}
|
||||
|
||||
/** Set a thread initializer for the given callback.
|
||||
The thread initializer indicates desired thread configuration when the
|
||||
given Callback is invoked on a native thread not yet attached to the
|
||||
@@ -1062,7 +1070,6 @@ public final class Native {
|
||||
*/
|
||||
public static void setCallbackThreadInitializer(Callback cb, CallbackThreadInitializer initializer) {
|
||||
CallbackReference.setCallbackThreadInitializer(cb, initializer);
|
||||
setCallbackOptions(cb, CB_HAS_INITIALIZER);
|
||||
}
|
||||
|
||||
|
||||
@@ -1152,7 +1159,7 @@ public final class Native {
|
||||
*/
|
||||
public static final int CB_NODETACH = 2;
|
||||
/** Indicates whether the callback has an initializer. */
|
||||
private static final int CB_HAS_INITIALIZER = 4;
|
||||
static final int CB_HAS_INITIALIZER = 4;
|
||||
|
||||
private static final int CVT_UNSUPPORTED = -1;
|
||||
private static final int CVT_DEFAULT = 0;
|
||||
@@ -1678,6 +1685,10 @@ public final class Native {
|
||||
|
||||
static native void setString(long addr, String value, boolean wide);
|
||||
|
||||
/** NOTE: no JNI references are created. */
|
||||
static native void setObject(long addr, Object object);
|
||||
static native Object getObject(long addr);
|
||||
|
||||
/**
|
||||
* Call the real native malloc
|
||||
* @param size size of the memory to be allocated
|
||||
@@ -1702,4 +1713,9 @@ public final class Native {
|
||||
* @return a direct ByteBuffer that accesses the memory being pointed to,
|
||||
*/
|
||||
public static native ByteBuffer getDirectByteBuffer(long addr, long length);
|
||||
|
||||
/** Indicate the desired attachment state for the current thread. */
|
||||
public static void detach(boolean detach) {
|
||||
setLastError(detach ? THREAD_DETACH : THREAD_ATTACH);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,11 +421,11 @@ public class NativeLibrary {
|
||||
/** Close the native library we're mapped to. */
|
||||
public void dispose() {
|
||||
synchronized(libraries) {
|
||||
libraries.remove(getName() + options);
|
||||
File file = getFile();
|
||||
if (file != null) {
|
||||
libraries.remove(file.getAbsolutePath() + options);
|
||||
libraries.remove(file.getName() + options);
|
||||
for (Iterator i=libraries.values().iterator();i.hasNext();) {
|
||||
Reference ref = (WeakReference)i.next();
|
||||
if (ref.get() == this) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized(this) {
|
||||
|
||||
@@ -464,8 +464,11 @@ public class Pointer {
|
||||
getArrayValue(offset, result, type.getComponentType());
|
||||
}
|
||||
else {
|
||||
result = getObject(offset);
|
||||
/*
|
||||
throw new IllegalArgumentException("Reading \""
|
||||
+ type + "\" from memory is not supported");
|
||||
*/
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -644,6 +647,13 @@ public class Pointer {
|
||||
return Native.getPointer(peer + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a native jobject pointer as a Java Object.
|
||||
*/
|
||||
Object getObject(long offset) {
|
||||
return Native.getObject(peer + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a ByteBuffer mapped to the memory pointed to by the pointer,
|
||||
* ensuring the buffer uses native byte order.
|
||||
@@ -886,7 +896,8 @@ v * @param wide whether to convert from a wide or standard C string
|
||||
setArrayValue(offset, value, type.getComponentType());
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Writing " + type + " to memory is not supported");
|
||||
setObject(offset, value);
|
||||
//throw new IllegalArgumentException("Writing " + type + " to memory is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,6 +1107,11 @@ v * @param wide whether to convert from a wide or standard C string
|
||||
Native.setPointer(peer + offset, value != null ? value.peer : 0);
|
||||
}
|
||||
|
||||
/** Write a Java Object as a native jobject pointer. */
|
||||
void setObject(long offset, Object value) {
|
||||
Native.setObject(peer + offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy string <code>value</code> to the location being pointed to. Copy
|
||||
* each element in <code>value</code>, converted to native encoding, at an
|
||||
|
||||
@@ -269,15 +269,14 @@ a single <code>callback</code> method with a signature that matches the
|
||||
function pointer required by the native code. The name of the method
|
||||
may be something other than "callback" only if there is only a single method
|
||||
in the interface which extends Callback or the class which implements
|
||||
{@link com.sun.jna.Callback}. The arguments and return value follow the same rules
|
||||
as for a
|
||||
direct function invocation.
|
||||
{@link com.sun.jna.Callback}. The arguments and return value follow the same
|
||||
rules as for a direct function invocation.
|
||||
<p>
|
||||
If the callback returns a <code>String</code> or <code>String[]</code>, the
|
||||
returned memory will be valid until the returned object is GC'd.
|
||||
<p>
|
||||
If your native code initializes function pointers within a struct, JNA will
|
||||
automatically generate an <code>Callback</code> instance matching the declared
|
||||
automatically generate a <code>Callback</code> instance matching the declared
|
||||
type. This enables you to easily call the function supplied by native code
|
||||
using proper Java syntax.
|
||||
<blockquote><code><pre>
|
||||
@@ -320,6 +319,26 @@ public interface CLibrary extends Library {
|
||||
}
|
||||
</pre></code></blockquote>
|
||||
|
||||
If you need control over the thread context in which a <code>Callback</code>
|
||||
operates, you can install a {@link com.sun.jna.CallbackThreadInitializer} for any given
|
||||
callback object. The first time the callback is called on a thread that is
|
||||
not currently attached to the VM, the initializer will be queried to determine
|
||||
how the thread should be set up. You can indicate the desired name, thread
|
||||
group, and daemon state for the thread, as well as indicating whether the
|
||||
thread should be left attached to the VM after callback exit. The latter
|
||||
improves performance if you know you will be getting multiple callbacks on the
|
||||
same thread, avoiding the need for the VM to generate multiple Java Thread
|
||||
objects for the same native thread. If you do leave the native thread
|
||||
attached, you should either ensure you detach it at some later point (by
|
||||
calling {@link com.sun.jna.Native#setCallbackOptions(Callback)} without
|
||||
{@link com.sun.jna.Native#CB_NODETACH}
|
||||
or return true from your
|
||||
{@link com.sun.jna.CallbackThreadInitializer#isDaemon(Callback)} method so
|
||||
that the native thread will not prevent the VM from exiting.<p/>
|
||||
If you don't need to otherwise customize the callback thread, you can simply
|
||||
call {@link com.sun.jna.Native#detach(boolean)} from within your callback to
|
||||
indicate whether the thread attachment should be maintained or not.<p/>
|
||||
|
||||
<a name="varargs"></a>
|
||||
<h3>Varargs</h3>
|
||||
The C varargs function definition may be mapped to a Java varargs method definition. For example,
|
||||
|
||||
@@ -905,37 +905,121 @@ public class CallbacksTest extends TestCase {
|
||||
assertEquals("Incorrect result of callback invocation", -2, result, 0);
|
||||
}
|
||||
|
||||
public void testNativeThreadAttachment() throws Exception {
|
||||
final boolean[] called = {false};
|
||||
final Set threads = new HashSet();
|
||||
int COUNT = 10;
|
||||
CallbackThreadInitializer init = new CallbackThreadInitializer() {
|
||||
public String getName() {
|
||||
System.out.println("Thread initializer called on " + Thread.currentThread());
|
||||
return CallbacksTest.this.getName();
|
||||
protected void callCallback(TestLibrary.VoidCallback cb,
|
||||
CallbackThreadInitializer cti,
|
||||
int repeat, int sleepms,
|
||||
int[] called) throws Exception {
|
||||
Native.setCallbackThreadInitializer(cb, cti);
|
||||
lib.callVoidCallbackThreaded(cb, repeat, sleepms);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
while (called[0] < repeat) {
|
||||
Thread.sleep(10);
|
||||
if (System.currentTimeMillis() - start > 5000) {
|
||||
fail("Timed out waiting for callback");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void testCallbackThreadDefaults() throws Exception {
|
||||
final int[] called = {0};
|
||||
final boolean[] daemon = {false};
|
||||
final String[] name = { null };
|
||||
final ThreadGroup[] group = { null };
|
||||
final Thread[] t = { null };
|
||||
|
||||
ThreadGroup testGroup = new ThreadGroup(getName());
|
||||
CallbackThreadInitializer init = new CallbackThreadInitializer();
|
||||
TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {
|
||||
public void callback() {
|
||||
System.out.println("in callback: " + Thread.currentThread());
|
||||
called[0] = true;
|
||||
threads.add(Thread.currentThread());
|
||||
Thread thread = Thread.currentThread();
|
||||
daemon[0] = thread.isDaemon();
|
||||
name[0] = thread.getName();
|
||||
group[0] = thread.getThreadGroup();
|
||||
t[0] = thread;
|
||||
++called[0];
|
||||
}
|
||||
};
|
||||
// TODO: check thread count
|
||||
// TODO: check thread name, group, daemon status
|
||||
Native.setCallbackThreadInitializer(cb, init);
|
||||
lib.callVoidCallbackThreaded(cb, COUNT, 1000);
|
||||
callCallback(cb, init, 1, 100, called);
|
||||
|
||||
// OSX: with one big sleep, we get different threads;
|
||||
// with smaller ones, the thread object is apparently re-used
|
||||
/*
|
||||
for (int i=0;i < COUNT;i++) {
|
||||
System.out.println("sleep 1s");
|
||||
Thread.sleep(1000);
|
||||
assertFalse("Callback thread default should not be attached as daemon", daemon[0]);
|
||||
}
|
||||
|
||||
public void testCustomizeCallbackThread() throws Exception {
|
||||
final int[] called = {0};
|
||||
final boolean[] daemon = {false};
|
||||
final String[] name = { null };
|
||||
final ThreadGroup[] group = { null };
|
||||
final Thread[] t = { null };
|
||||
|
||||
ThreadGroup testGroup = new ThreadGroup(getName());
|
||||
CallbackThreadInitializer init = new CallbackThreadInitializer(true, true, getName(), testGroup);
|
||||
TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {
|
||||
public void callback() {
|
||||
Thread thread = Thread.currentThread();
|
||||
daemon[0] = thread.isDaemon();
|
||||
name[0] = thread.getName();
|
||||
group[0] = thread.getThreadGroup();
|
||||
t[0] = thread;
|
||||
++called[0];
|
||||
}
|
||||
};
|
||||
callCallback(cb, init, 1, 100, called);
|
||||
|
||||
assertTrue("Callback thread not attached as daemon", daemon[0]);
|
||||
assertEquals("Wrong thread name", getName(), name[0]);
|
||||
assertEquals("Wrong thread group", testGroup, group[0]);
|
||||
}
|
||||
|
||||
public void testCallbackThreadPersistence() throws Exception {
|
||||
final int[] called = {0};
|
||||
final Set threads = new HashSet();
|
||||
|
||||
ThreadGroup testGroup = new ThreadGroup(getName());
|
||||
CallbackThreadInitializer init = new CallbackThreadInitializer(true, false, getName(), testGroup);
|
||||
TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {
|
||||
public void callback() {
|
||||
threads.add(Thread.currentThread());
|
||||
++called[0];
|
||||
}
|
||||
};
|
||||
callCallback(cb, init, 10, 100, called);
|
||||
|
||||
assertEquals("Should only map a single Java Thread", 1, threads.size());
|
||||
}
|
||||
|
||||
public void testDynamicCallbackThreadPersistence() throws Exception {
|
||||
final int[] called = {0};
|
||||
final Set threads = new HashSet();
|
||||
|
||||
ThreadGroup testGroup = new ThreadGroup(getName());
|
||||
CallbackThreadInitializer init = new CallbackThreadInitializer(true, true, getName(), testGroup);
|
||||
final int COUNT = 10;
|
||||
TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {
|
||||
public void callback() {
|
||||
threads.add(Thread.currentThread());
|
||||
// detach on final invocation
|
||||
int count = called[0] + 1;
|
||||
if (count == 1) {
|
||||
Native.detach(false);
|
||||
}
|
||||
else if (count == COUNT) {
|
||||
Native.detach(true);
|
||||
}
|
||||
called[0] = count;
|
||||
}
|
||||
};
|
||||
callCallback(cb, init, COUNT, 100, called);
|
||||
|
||||
assertEquals("Should only map a single Java Thread", 1, threads.size());
|
||||
Thread thread = (Thread)threads.iterator().next();
|
||||
long start = System.currentTimeMillis();
|
||||
while (thread.isAlive()) {
|
||||
Thread.sleep(10);
|
||||
if (System.currentTimeMillis() - start > 5000) {
|
||||
fail("Timed out waiting for callback thread to die");
|
||||
}
|
||||
}
|
||||
*/
|
||||
Thread.sleep(10000);
|
||||
}
|
||||
|
||||
public static void main(java.lang.String[] argList) {
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário