mHead = new AtomicReference<>();
+
+ public void push(Destructor newHead) {
+ Destructor oldHead;
+ do {
+ oldHead = mHead.get();
+ newHead.next = oldHead;
+ } while (!mHead.compareAndSet(oldHead, newHead));
+ }
+
+ public void transferAllToList() {
+ Destructor current = mHead.getAndSet(null);
+ while (current != null) {
+ Destructor next = current.next;
+ sDestructorList.enqueue(current);
+ current = next;
+ }
+ }
+ }
+
+ /** A doubly-linked list of Destructors. */
+ private static class DestructorList {
+ private Destructor mHead;
+
+ public DestructorList() {
+ mHead = new Terminus();
+ mHead.next = new Terminus();
+ mHead.next.previous = mHead;
+ }
+
+ public void enqueue(Destructor current) {
+ current.next = mHead.next;
+ mHead.next = current;
+
+ current.next.previous = current;
+ current.previous = mHead;
+ }
+
+ private static void drop(Destructor current) {
+ current.next.previous = current.previous;
+ current.previous.next = current.next;
+ }
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/jni/HybridClassBase.java b/ReactAndroid/src/main/java/com/facebook/jni/HybridClassBase.java
new file mode 100644
index 0000000000..a20b92b8f1
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/jni/HybridClassBase.java
@@ -0,0 +1,8 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+import com.facebook.proguard.annotations.DoNotStrip;
+
+@DoNotStrip
+public abstract class HybridClassBase extends HybridData {
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java b/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java
index e0b864b08d..9ace5f3ddc 100644
--- a/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java
+++ b/ReactAndroid/src/main/java/com/facebook/jni/HybridData.java
@@ -2,6 +2,8 @@
package com.facebook.jni;
+import android.util.Log;
+
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@@ -10,11 +12,9 @@ import com.facebook.soloader.SoLoader;
*
* NB: THREAD SAFETY
*
- * {@link #dispose} deletes the corresponding native object on whatever thread
- * the method is called on. In the common case when this is called by
- * HybridData#finalize(), this will be called on the system finalizer
- * thread. If you manually call resetNative() on the Java object, the C++
- * object will be deleted synchronously on that thread.
+ * {@link #resetNative} deletes the corresponding native object synchronously on whatever thread
+ * the method is called on. Otherwise, deletion will occur on the {@link DestructorThread}
+ * thread.
*/
@DoNotStrip
public class HybridData {
@@ -23,27 +23,57 @@ public class HybridData {
SoLoader.loadLibrary("fb");
}
- // Private C++ instance
@DoNotStrip
- private long mNativePointer = 0;
+ private Destructor mDestructor = new Destructor(this);
/**
* To explicitly delete the instance, call resetNative(). If the C++
* instance is referenced after this is called, a NullPointerException will
* be thrown. resetNative() may be called multiple times safely. Because
- * {@link #finalize} calls resetNative, the instance will not leak if this is
+ * the {@link DestructorThread} also calls resetNative, the instance will not leak if this is
* not called, but timing of deletion and the thread the C++ dtor is called
* on will be at the whim of the Java GC. If you want to control the thread
* and timing of the destructor, you should call resetNative() explicitly.
*/
- public native void resetNative();
-
- protected void finalize() throws Throwable {
- resetNative();
- super.finalize();
+ public synchronized void resetNative() {
+ mDestructor.destruct();
}
+ /**
+ * N.B. Thread safety.
+ * If you call isValid from a different thread than {@link #resetNative()} then be sure to
+ * do so while synchronizing on the hybrid. For example:
+ *
+ * synchronized(hybrid) {
+ * if (hybrid.isValid) {
+ * // Do stuff.
+ * }
+ * }
+ *
+ */
public boolean isValid() {
- return mNativePointer != 0;
+ return mDestructor.mNativePointer != 0;
+ }
+
+ public static class Destructor extends DestructorThread.Destructor {
+
+ // Private C++ instance
+ @DoNotStrip
+ private long mNativePointer;
+
+ Destructor(Object referent) {
+ super(referent);
+ }
+
+ @Override
+ void destruct() {
+ // When invoked from the DestructorThread instead of resetNative,
+ // the DestructorThread has exclusive ownership of the HybridData
+ // so synchronization is not necessary.
+ deleteNative(mNativePointer);
+ mNativePointer = 0;
+ }
+
+ static native void deleteNative(long pointer);
}
}
diff --git a/ReactAndroid/src/main/java/com/facebook/jni/JniTerminateHandler.java b/ReactAndroid/src/main/java/com/facebook/jni/JniTerminateHandler.java
new file mode 100644
index 0000000000..29f66003ae
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/jni/JniTerminateHandler.java
@@ -0,0 +1,15 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+public class JniTerminateHandler {
+ public static void handleTerminate(Throwable t) throws Throwable {
+ Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
+ if (h == null) {
+ // Odd. Let the default std::terminate_handler deal with it.
+ return;
+ }
+ h.uncaughtException(Thread.currentThread(), t);
+ // That should exit. If it doesn't, let the default handler deal with it.
+ }
+}