Skip to content

Commit

Permalink
Add missing Java files to RN fbjni sync
Browse files Browse the repository at this point in the history
Reviewed By: mhorowitz

Differential Revision: D5129224

fbshipit-source-id: d9fb5f95505f6be7d3d87ead67dbfaa951c03434
  • Loading branch information
javache authored and facebook-github-bot committed May 30, 2017
1 parent b11dc39 commit f0e4a6c
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.jni;

import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;

/**
* Utility class to determine CPU capabilities
*/
@DoNotStrip
public class CpuCapabilitiesJni {

static {
SoLoader.loadLibrary("fb");
}

@DoNotStrip
public static native boolean nativeDeviceSupportsNeon();

@DoNotStrip
public static native boolean nativeDeviceSupportsVFPFP16();

@DoNotStrip
public static native boolean nativeDeviceSupportsX86();

}
132 changes: 132 additions & 0 deletions ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2004-present Facebook. All Rights Reserved.

package com.facebook.jni;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.atomic.AtomicReference;

/**
* A thread which invokes the "destruct" routine for objects after they have been garbage collected.
*
* An object which needs to be destructed should create a static subclass of {@link Destructor}.
* Once the referent object is garbage collected, the DestructorThread will callback to the
* {@link Destructor#destruct()} method.
*
* The underlying thread in DestructorThread starts when the first Destructor is constructed
* and then runs indefinitely.
*/
public class DestructorThread {

/**
* N.B The Destructor <b>SHOULD NOT</b> refer back to its referent object either explicitly or
* implicitly (for example, as a non-static inner class). This will create a reference cycle where
* the referent object will never be garbage collected.
*/
public abstract static class Destructor extends PhantomReference<Object> {

private Destructor next;
private Destructor previous;

Destructor(Object referent) {
super(referent, sReferenceQueue);
sDestructorStack.push(this);
}

private Destructor() {
super(null, sReferenceQueue);
}

/** Callback which is invoked when the original object has been garbage collected. */
abstract void destruct();
}

/** A list to keep all active Destructors in memory confined to the Destructor thread. */
private static DestructorList sDestructorList;
/** A thread safe stack where new Destructors are placed before being add to sDestructorList. */
private static DestructorStack sDestructorStack;
private static ReferenceQueue sReferenceQueue;
private static Thread sThread;

static {
sDestructorStack = new DestructorStack();
sReferenceQueue = new ReferenceQueue();
sDestructorList = new DestructorList();
sThread = new Thread("HybridData DestructorThread") {
@Override
public void run() {
while (true) {
try {
Destructor current = (Destructor) sReferenceQueue.remove();
current.destruct();

// If current is in the sDestructorStack,
// transfer all the Destructors in the stack to the list.
if (current.previous == null) {
sDestructorStack.transferAllToList();
}

DestructorList.drop(current);
} catch (InterruptedException e) {
// Continue. This thread should never be terminated.
}
}
}
};

sThread.start();
}

private static class Terminus extends Destructor {
@Override
void destruct() {
throw new IllegalStateException("Cannot destroy Terminus Destructor.");
}
}

/** This is a thread safe, lock-free Treiber-like Stack of Destructors. */
private static class DestructorStack {
private AtomicReference<Destructor> 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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
}
58 changes: 44 additions & 14 deletions ReactAndroid/src/main/java/com/facebook/jni/HybridData.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

package com.facebook.jni;

import android.util.Log;

import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;

Expand All @@ -10,11 +12,9 @@
*
* 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 {
Expand All @@ -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:
* <pre><code>
* synchronized(hybrid) {
* if (hybrid.isValid) {
* // Do stuff.
* }
* }
* </code></pre>
*/
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);
}
}
Original file line number Diff line number Diff line change
@@ -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.
}
}

0 comments on commit f0e4a6c

Please sign in to comment.