Skip to content

Commit

Permalink
* Switch to AttachCurrentThreadAsDaemon() when attaching native th…
Browse files Browse the repository at this point in the history
…reads on callback (pull #561)
  • Loading branch information
masterav10 authored Mar 11, 2022
1 parent 6480444 commit 9a57b0d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.classpath
.project
.settings
.factorypath

# IntelliJ
*.iml
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Switch to `AttachCurrentThreadAsDaemon()` when attaching native threads on callback ([pull #561](https://github.com/bytedeco/javacpp/pull/561))
* Add to `InfoMap` default pointer and value types for integer types in `std::` namespace
* Fix Android build properties for NDK r23b

Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/bytedeco/javacpp/tools/Generator.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println(" #include <malloc.h>");
out.println(" #include <sys/types.h>");
out.println(" #include <sys/stat.h>");
out.println(" #include <sys/syscall.h>");
out.println(" #include <sys/sysinfo.h>");
out.println(" #include <fcntl.h>");
out.println(" #include <unistd.h>");
Expand Down Expand Up @@ -1450,11 +1451,11 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
out.println(" sprintf(name, \"JavaCPP Thread ID %lu\", GetCurrentThreadId());");
out.println("#elif defined(__APPLE__)");
out.println(" sprintf(name, \"JavaCPP Thread ID %u\", pthread_mach_thread_np(pthread_self()));");
out.println("#else");
out.println(" sprintf(name, \"JavaCPP Thread ID %lu\", pthread_self());");
out.println("#elif defined(__linux__)");
out.println(" sprintf(name, \"JavaCPP Thread ID %ld\", syscall(SYS_gettid));");
out.println("#endif");
out.println(" args.name = name;");
out.println(" if (vm->AttachCurrentThread(env2, &args) != JNI_OK) {");
out.println(" if (vm->AttachCurrentThreadAsDaemon(env2, &args) != JNI_OK) {");
out.println(" JavaCPP_log(\"Could not attach the JavaVM to the current thread.\");");
out.println(" *env = NULL;");
out.println(" goto done;");
Expand Down
99 changes: 99 additions & 0 deletions src/test/java/org/bytedeco/javacpp/ThreadTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.bytedeco.javacpp;

import static org.junit.Assert.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.bytedeco.javacpp.annotation.Platform;
import org.bytedeco.javacpp.annotation.Virtual;
import org.bytedeco.javacpp.tools.Builder;
import org.junit.BeforeClass;
import org.junit.Test;

/**
* This test case verifies that threads attached from the native layer are
* configured as daemons and can remain attached when the appropriate property
* is specified.
* <p>
* You would want this behavior when you have a callback that is called with a
* high frequency.
*
* @author Dan Avila
*
*/
@Platform(compiler = "cpp11", define = "NO_JNI_DETACH_THREAD", include = "ThreadTest.h")
public class ThreadTest {
public static class Callback extends Pointer {
/** Default native constructor. */
public Callback() { super((Pointer) null); allocate(); }
/** Native array allocator. Access with {@link Pointer#position(long)}. */
public Callback(long size){ super((Pointer) null); allocateArray(size); }
/** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */
public Callback(Pointer p) { super(p); }

private native void allocate();
private native void allocateArray(long size);

@Override public Callback position(long position) {
return (Callback)super.position(position);
}

@Override public Callback getPointer(long i) {
return new Callback((Pointer)this).offsetAddress(i);
}

@Virtual(true) public native void callback(int value);
}

static native void run(Callback callback, int count);

@BeforeClass public static void setUpClass() throws Exception {
System.out.println("Builder");
Class c = ThreadTest.class;
Builder builder = new Builder().classesOrPackages(c.getName());
File[] outputFiles = builder.build();

System.out.println("Loader");
Loader.load(c);
}

@Test public void testJNIThreadAttachFromNativeCallbacks() {
final int count = 10;
final List<Integer> callbackValueRefs = new ArrayList<>();
final List<Thread> threadRefs = new ArrayList<>();

Callback callback = new Callback() {
@Override public void callback(int value) {
System.out.println("Callback from " + Thread.currentThread());
callbackValueRefs.add(Integer.valueOf(value));
threadRefs.add(Thread.currentThread());
}
};

run(callback, count);

assertTrue(callbackValueRefs.size() == count);
assertTrue(threadRefs.size() == count);

for (int i = 0; i < count; i++) {
int value = callbackValueRefs.get(i);
Thread callbackThread = threadRefs.get(i);

assertEquals("Callback was not called", i + 1, value);

assertNotEquals("Callback thread is not main", Thread.currentThread(), callbackThread);
assertTrue("Callback thread is daemon", callbackThread.isDaemon());
}

for (int i = 1; i < count; i++) {
Thread cbThread1 = threadRefs.get(i - 1);
Thread cbThread2 = threadRefs.get(i);

assertEquals(cbThread1, cbThread2);
}
}
}
18 changes: 18 additions & 0 deletions src/test/resources/org/bytedeco/javacpp/ThreadTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include <thread>

class Callback {
public:
virtual void callback(int value) = 0;
};

static void doIt(Callback* callback, int value) {
for(int i = 1; i <= value; i++) {
callback->callback(i);
}
}

static void run(Callback* callback, int count) {
std::thread thread(doIt, callback, count);

thread.join();
}

0 comments on commit 9a57b0d

Please sign in to comment.