functional detach switch from callback

Esse commit está contido em:
Timothy Wall
2011-09-09 10:17:30 -04:00
commit 2841c3aaf1
14 arquivos alterados com 305 adições e 106 exclusões
+2 -2
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
Ver Arquivo
@@ -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
+41 -33
Ver Arquivo
@@ -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);
+1 -1
Ver Arquivo
@@ -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; }
}
}
+18 -2
Ver Arquivo
@@ -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);
}
}
+5 -5
Ver Arquivo
@@ -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) {
+17 -1
Ver Arquivo
@@ -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
+23 -4
Ver Arquivo
@@ -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,
+108 -24
Ver Arquivo
@@ -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) {