Upgrade fbjni

Summary:
Upgrades Yoga’s copy of *fbjni* to the latest version.

This will enable us

- to move from `finalize()` to `PhantomReference` to deallocate native memory, with the potential of making GC more efficient.
- to remove the internal dependency to *libfb,* allowing apps without an own dependency to ship less code

Reviewed By: passy

Differential Revision: D16220924

fbshipit-source-id: e8233fe2b5403946ff51f43cb6def558ded52fda
This commit is contained in:
David Aurelio
2019-07-17 06:52:55 -07:00
committed by Facebook Github Bot
parent be305b5d0f
commit 59d680f4e9
91 changed files with 2512 additions and 3200 deletions

View File

@@ -1,30 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
/**
* A Java Object that has native memory allocated corresponding to this instance.
*
* NB: THREAD SAFETY (this comment also exists at Countable.cpp)
*
* {@link #dispose} deletes the corresponding native object on whatever thread the method is called
* on. In the common case when this is called by Countable#finalize(), this will be called on the
* system finalizer thread. If you manually call dispose on the Java object, the native object
* will be deleted synchronously on that thread.
*/
@DoNotStrip
public class Countable {
// Private C++ instance
@DoNotStrip
private long mInstance = 0;
public native void dispose();
protected void finalize() throws Throwable {
dispose();
super.finalize();
}
}

View File

@@ -1,13 +1,12 @@
/*
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class CppException extends RuntimeException {

View File

@@ -1,13 +1,12 @@
/*
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class CppSystemErrorException extends CppException {

View File

@@ -0,0 +1,138 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
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.
*
* <p>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.
*
* <p>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;
public 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. */
protected abstract void destruct();
}
/** A list to keep all active Destructors in memory confined to the Destructor thread. */
private static final DestructorList sDestructorList;
/** A thread safe stack where new Destructors are placed before being add to sDestructorList. */
private static final DestructorStack sDestructorStack;
private static final ReferenceQueue sReferenceQueue;
private static final 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
protected 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 final 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 final 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;
}
}
}

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public abstract class HybridClassBase extends HybridData {}

View File

@@ -1,44 +1,77 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
/**
* This object holds a native C++ member for hybrid Java/C++ objects.
*
* NB: THREAD SAFETY
* <p>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.
* <p>{@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 {
// Private C++ instance
@DoNotStrip
private long mNativePointer = 0;
static {
SoLoader.loadLibrary("fbjni");
}
@DoNotStrip 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
* 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.
* 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 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
protected 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);
}
}

View File

@@ -1,31 +1,26 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
import java.util.Iterator;
import javax.annotation.Nullable;
import java.util.Iterator;
/**
* To iterate over an Iterator from C++ requires two calls per entry: hasNext()
* and next(). This helper reduces it to one call and one field get per entry.
* It does not use a generic argument, since in C++, the types will be erased,
* anyway. This is *not* a {@link java.util.Iterator}.
* To iterate over an Iterator from C++ requires two calls per entry: hasNext() and next(). This
* helper reduces it to one call and one field get per entry. It does not use a generic argument,
* since in C++, the types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
*/
@DoNotStrip
public class IteratorHelper {
private final Iterator mIterator;
// This is private, but accessed via JNI.
@DoNotStrip
private @Nullable Object mElement;
@DoNotStrip private @Nullable Object mElement;
@DoNotStrip
public IteratorHelper(Iterator iterator) {
@@ -38,8 +33,8 @@ public class IteratorHelper {
}
/**
* Moves the helper to the next entry in the map, if any. Returns true iff
* there is an entry to read.
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
* read.
*/
@DoNotStrip
boolean hasNext() {

View File

@@ -1,24 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import javax.annotation.Nullable;
import com.facebook.jni.annotations.DoNotStrip;
import java.util.Iterator;
import java.util.Map;
import com.facebook.proguard.annotations.DoNotStrip;
import javax.annotation.Nullable;
/**
* To iterate over a Map from C++ requires four calls per entry: hasNext(),
* next(), getKey(), getValue(). This helper reduces it to one call and two
* field gets per entry. It does not use a generic argument, since in C++, the
* types will be erased, anyway. This is *not* a {@link java.util.Iterator}.
* To iterate over a Map from C++ requires four calls per entry: hasNext(), next(), getKey(),
* getValue(). This helper reduces it to one call and two field gets per entry. It does not use a
* generic argument, since in C++, the types will be erased, anyway. This is *not* a {@link
* java.util.Iterator}.
*/
@DoNotStrip
public class MapIteratorHelper {
@@ -32,8 +29,8 @@ public class MapIteratorHelper {
}
/**
* Moves the helper to the next entry in the map, if any. Returns true iff
* there is an entry to read.
* Moves the helper to the next entry in the map, if any. Returns true iff there is an entry to
* read.
*/
@DoNotStrip
boolean hasNext() {

View File

@@ -1,18 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
/**
* A Runnable that has a native run implementation.
*/
/** A Runnable that has a native run implementation. */
@DoNotStrip
public class NativeRunnable implements Runnable {

View File

@@ -1,11 +1,20 @@
// Copyright 2004-present Facebook. All Rights Reserved.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
@DoNotStrip
public class ThreadScopeSupport {
static {
SoLoader.loadLibrary("fbjni");
}
// This is just used for ThreadScope::withClassLoader to have a java function
// in the stack so that jni has access to the correct classloader.
@DoNotStrip

View File

@@ -1,13 +1,12 @@
/*
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStrip;
@DoNotStrip
public class UnknownCppException extends CppException {

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package com.facebook.jni.annotations;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Add this annotation to a class, method, or field to instruct Proguard to not strip it out.
*
* This is useful for methods called via reflection that could appear as unused to Proguard.
*/
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR })
@Retention(CLASS)
public @interface DoNotStrip {
}