Make SafeReleaseJniRef more generic (#50376)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50376

This makes a couple of tweaks to `SafeReleaseJniRef` to let it be used in more places:

1. Add a default ctor for null state
2. Bridge `get()` to return raw JNI ref, for use when calling Java functions directly (compared to current use case of hybrid objects)
3. Keep JNI environment attached to thread longer term, instead of repeated attach/detach, to allow use for higher frequency objects.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D71933838

fbshipit-source-id: 622cc70d2d7483475406314abcbcf92d3d2ff227
This commit is contained in:
Nick Gerleman
2025-03-28 12:32:20 -07:00
committed by Facebook GitHub Bot
parent 7441127040
commit e861949ccd
2 changed files with 42 additions and 1 deletions
@@ -0,0 +1,30 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <react/jni/SafeReleaseJniRef.h>
namespace facebook::react {
void ensureThreadDurationJNIEnvAttached() {
// Attaching and detaching the thread to the JNI environment may take
// hundreds of microseconds, and we attach to a very limited number of
// threads (in practice, just the Hermes GC), so we only perform
// registration once, detaching before the thread is destroyed.
//
// > In Android 2.0 (Eclair) and higher you can use pthread_key_create() to
// > define a destructor function that will be called before the thread
// > exits, and call DetachCurrentThread() from there.
// https://github.com/facebookincubator/fbjni/blob/7b7efda0d49b956acf1d3307510e3c73fc55b404/cxx/fbjni/detail/Environment.h#L101
// https://developer.android.com/training/articles/perf-jni#threads
static thread_local std::optional<jni::ThreadScope> threadScope;
if (jni::detail::currentOrNull() == nullptr) {
threadScope.emplace();
}
}
} // namespace facebook::react
@@ -12,6 +12,12 @@
namespace facebook::react {
/**
* Ensures that the current thread has an attached JNI environment, setting one
* up to be detached at the end of the thread's lifetime if not.
*/
void ensureThreadDurationJNIEnvAttached();
/**
* A wrapper around a JNI reference (e.g. jni::global_ref<>) that makes it safe
* to destroy (decrement refcount) from any thread. This wrapping is necessary
@@ -29,6 +35,7 @@ class SafeReleaseJniRef {
using T = std::remove_reference<decltype(*std::declval<RefT>())>::type;
public:
SafeReleaseJniRef() = default;
/* explicit */ SafeReleaseJniRef(RefT ref) : ref_(std::move(ref)) {}
SafeReleaseJniRef(const SafeReleaseJniRef& other) = default;
SafeReleaseJniRef(SafeReleaseJniRef&& other) = default;
@@ -37,7 +44,7 @@ class SafeReleaseJniRef {
~SafeReleaseJniRef() {
if (ref_) {
jni::ThreadScope ts;
ensureThreadDurationJNIEnvAttached();
ref_.reset();
}
}
@@ -66,6 +73,10 @@ class SafeReleaseJniRef {
return ref_;
}
RefT::javaobject get() const {
return ref_.get();
}
private:
RefT ref_;
};