Fix CC collector thread flip and JNI critical section deadlocks.
There's a bug in the synchronization between the CC collector's thread
flip and JNI critical sections where it incorrectly attempts to make
the synchronization scheme to be writer (GC) preference to avoid
starvation of GC in the presence of frequent JNI critical section
enter/exit. This could cause a deadlock between them if a thread
enters a nested JNI critical section after a thread flip occurs. This
is reproduced in the added test.
The fix is to use a thread local disable counter in addition to the
global counter to detect a nested enter by the same thread and avoid
waiting if nested.
Bug: 19235243
Bug: 12687968
Change-Id: Idf7720a6906c9ea508219935af3727f76680d2d8
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index ee3a3b9..5c39ede 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -38,6 +38,7 @@
testNewStringObject();
testRemoveLocalObject();
testProxyGetMethodID();
+ testJniCriticalSectionAndGc();
}
private static native void testFindClassOnAttachedNativeThread();
@@ -222,6 +223,35 @@
}
private static native long testGetMethodID(Class<?> c);
+
+ // Exercise GC and JNI critical sections in parallel.
+ private static void testJniCriticalSectionAndGc() {
+ Thread runGcThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < 10; ++i) {
+ Runtime.getRuntime().gc();
+ }
+ }
+ });
+ Thread jniCriticalThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final int arraySize = 32;
+ byte[] array0 = new byte[arraySize];
+ byte[] array1 = new byte[arraySize];
+ enterJniCriticalSection(arraySize, array0, array1);
+ }
+ });
+ jniCriticalThread.start();
+ runGcThread.start();
+ try {
+ jniCriticalThread.join();
+ runGcThread.join();
+ } catch (InterruptedException ignored) {}
+ }
+
+ private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array);
}
class JniCallNonvirtualTest {