xref: /aosp_15_r20/external/fbjni/cxx/fbjni/detail/Environment.h (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
1*65c59e02SInna Palant /*
2*65c59e02SInna Palant  * Copyright (c) Facebook, Inc. and its affiliates.
3*65c59e02SInna Palant  *
4*65c59e02SInna Palant  * Licensed under the Apache License, Version 2.0 (the "License");
5*65c59e02SInna Palant  * you may not use this file except in compliance with the License.
6*65c59e02SInna Palant  * You may obtain a copy of the License at
7*65c59e02SInna Palant  *
8*65c59e02SInna Palant  *     http://www.apache.org/licenses/LICENSE-2.0
9*65c59e02SInna Palant  *
10*65c59e02SInna Palant  * Unless required by applicable law or agreed to in writing, software
11*65c59e02SInna Palant  * distributed under the License is distributed on an "AS IS" BASIS,
12*65c59e02SInna Palant  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*65c59e02SInna Palant  * See the License for the specific language governing permissions and
14*65c59e02SInna Palant  * limitations under the License.
15*65c59e02SInna Palant  */
16*65c59e02SInna Palant 
17*65c59e02SInna Palant #pragma once
18*65c59e02SInna Palant #include <jni.h>
19*65c59e02SInna Palant #include <functional>
20*65c59e02SInna Palant #include <string>
21*65c59e02SInna Palant 
22*65c59e02SInna Palant namespace facebook {
23*65c59e02SInna Palant namespace jni {
24*65c59e02SInna Palant 
25*65c59e02SInna Palant // Keeps a thread-local reference to the current thread's JNIEnv.
26*65c59e02SInna Palant struct Environment {
27*65c59e02SInna Palant   // Throws a std::runtime_error if this thread isn't attached to the JVM
28*65c59e02SInna Palant   // TODO(T6594868) Benchmark against raw JNI access
29*65c59e02SInna Palant   static JNIEnv* current();
30*65c59e02SInna Palant   static void initialize(JavaVM* vm);
31*65c59e02SInna Palant 
32*65c59e02SInna Palant   // There are subtle issues with calling the next functions directly. It is
33*65c59e02SInna Palant   // much better to always use a ThreadScope to manage attaching/detaching for
34*65c59e02SInna Palant   // you.
35*65c59e02SInna Palant   static JNIEnv* ensureCurrentThreadIsAttached();
36*65c59e02SInna Palant 
37*65c59e02SInna Palant   // To check if a Java VM is available at all in this environment.
38*65c59e02SInna Palant   // Note that this doesn't check if it is attached to this thread,
39*65c59e02SInna Palant   // it checks whether one is available at all.
40*65c59e02SInna Palant   static bool isGlobalJvmAvailable();
41*65c59e02SInna Palant };
42*65c59e02SInna Palant 
43*65c59e02SInna Palant namespace detail {
44*65c59e02SInna Palant 
45*65c59e02SInna Palant // This will return null the thread isn't attached to the VM, or if
46*65c59e02SInna Palant // fbjni has never been initialized with a VM at all.  You probably
47*65c59e02SInna Palant // shouldn't be using this.
48*65c59e02SInna Palant JNIEnv* currentOrNull();
49*65c59e02SInna Palant 
50*65c59e02SInna Palant // This will return the cached JNIEnv* and the current state of
51*65c59e02SInna Palant // attaching the thread
52*65c59e02SInna Palant JNIEnv* cachedWithAttachmentState(bool& isAttaching);
53*65c59e02SInna Palant 
54*65c59e02SInna Palant /**
55*65c59e02SInna Palant  * If there's thread-local data, it's a pointer to one of these.  The
56*65c59e02SInna Palant  * instance is a member of JniEnvCacher or ThreadScope, and lives on
57*65c59e02SInna Palant  * the stack.
58*65c59e02SInna Palant  */
59*65c59e02SInna Palant struct TLData {
60*65c59e02SInna Palant   // This is modified only by JniEnvCacher, and is guaranteed to be
61*65c59e02SInna Palant   // valid if set, and refer to an env which originated from a JNI
62*65c59e02SInna Palant   // call into C++.
63*65c59e02SInna Palant   JNIEnv* env;
64*65c59e02SInna Palant   // This is modified only by ThreadScope, and is set only if an
65*65c59e02SInna Palant   // instance of ThreadScope which attached is on the stack.
66*65c59e02SInna Palant   bool attached;
67*65c59e02SInna Palant };
68*65c59e02SInna Palant 
69*65c59e02SInna Palant /**
70*65c59e02SInna Palant  * RAII object which manages a cached JNIEnv* value.  A Value is only
71*65c59e02SInna Palant  * cached if it is guaranteed safe, which means when C++ is called
72*65c59e02SInna Palant  * from a registered fbjni function.
73*65c59e02SInna Palant  */
74*65c59e02SInna Palant class JniEnvCacher {
75*65c59e02SInna Palant  public:
76*65c59e02SInna Palant   JniEnvCacher(JNIEnv* env);
77*65c59e02SInna Palant   JniEnvCacher(JniEnvCacher&) = delete;
78*65c59e02SInna Palant   JniEnvCacher(JniEnvCacher&&) = default;
79*65c59e02SInna Palant   JniEnvCacher& operator=(JniEnvCacher&) = delete;
80*65c59e02SInna Palant   JniEnvCacher& operator=(JniEnvCacher&&) = delete;
81*65c59e02SInna Palant   ~JniEnvCacher();
82*65c59e02SInna Palant 
83*65c59e02SInna Palant  private:
84*65c59e02SInna Palant   // If this flag is set, then, this object needs to clear the cache.
85*65c59e02SInna Palant   bool thisCached_;
86*65c59e02SInna Palant 
87*65c59e02SInna Palant   // The thread local pointer may point here.
88*65c59e02SInna Palant   detail::TLData data_;
89*65c59e02SInna Palant };
90*65c59e02SInna Palant 
91*65c59e02SInna Palant } // namespace detail
92*65c59e02SInna Palant 
93*65c59e02SInna Palant /**
94*65c59e02SInna Palant  * RAII Object that attaches a thread to the JVM. Failing to detach from a
95*65c59e02SInna Palant  * thread before it exits will cause a crash, as will calling Detach an extra
96*65c59e02SInna Palant  * time, and this guard class helps keep that straight. In addition, it
97*65c59e02SInna Palant  * remembers whether it performed the attach or not, so it is safe to nest it
98*65c59e02SInna Palant  * with itself or with non-fbjni code that manages the attachment correctly.
99*65c59e02SInna Palant  *
100*65c59e02SInna Palant  * Potential concerns:
101*65c59e02SInna Palant  *  - Attaching to the JVM is fast (~100us on MotoG), but ideally you would
102*65c59e02SInna Palant  * attach while the app is not busy.
103*65c59e02SInna Palant  *  - Having a thread detach at arbitrary points is not safe in Dalvik; you need
104*65c59e02SInna Palant  * to be sure that there is no Java code on the current stack or you run the
105*65c59e02SInna Palant  * risk of a crash like: ERROR: detaching thread with interp frames (count=18)
106*65c59e02SInna Palant  *    (More detail at
107*65c59e02SInna Palant  * https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo) ThreadScope
108*65c59e02SInna Palant  * won't do a detach if the thread was already attached before the guard is
109*65c59e02SInna Palant  *    instantiated, but there's probably some usage that could trip this up.
110*65c59e02SInna Palant  *  - Newly attached C++ threads only get the bootstrap class loader -- i.e.
111*65c59e02SInna Palant  * java language classes, not any of our application's classes. This will be
112*65c59e02SInna Palant  * different behavior than threads that were initiated on the Java side. A
113*65c59e02SInna Palant  * workaround is to pass a global reference for a class or instance to the new
114*65c59e02SInna Palant  * thread; this bypasses the need for the class loader. (See
115*65c59e02SInna Palant  * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)
116*65c59e02SInna Palant  *    If you need access to the application's classes, you can use
117*65c59e02SInna Palant  * ThreadScope::WithClassLoader.
118*65c59e02SInna Palant  *  - If fbjni has never been initialized, there will be no JavaVM object to
119*65c59e02SInna Palant  * attach with. In that case, a std::runtime_error will be thrown.  This is only
120*65c59e02SInna Palant  * likely to happen in a standalone C++ application, or if
121*65c59e02SInna Palant  * Environment::initialize is not used.
122*65c59e02SInna Palant  */
123*65c59e02SInna Palant class ThreadScope {
124*65c59e02SInna Palant  public:
125*65c59e02SInna Palant   ThreadScope();
126*65c59e02SInna Palant   ThreadScope(ThreadScope&) = delete;
127*65c59e02SInna Palant   ThreadScope(ThreadScope&&) = default;
128*65c59e02SInna Palant   ThreadScope& operator=(ThreadScope&) = delete;
129*65c59e02SInna Palant   ThreadScope& operator=(ThreadScope&&) = delete;
130*65c59e02SInna Palant   ~ThreadScope();
131*65c59e02SInna Palant 
132*65c59e02SInna Palant   /**
133*65c59e02SInna Palant    * This runs the closure in a scope with fbjni's classloader. This should be
134*65c59e02SInna Palant    * the same classloader as the rest of the application and thus anything
135*65c59e02SInna Palant    * running in the closure will have access to the same classes as in a normal
136*65c59e02SInna Palant    * java-create thread.
137*65c59e02SInna Palant    */
138*65c59e02SInna Palant   static void WithClassLoader(std::function<void()>&& runnable);
139*65c59e02SInna Palant 
140*65c59e02SInna Palant   static void OnLoad();
141*65c59e02SInna Palant 
142*65c59e02SInna Palant  private:
143*65c59e02SInna Palant   // If this flag is set, then this object needs to detach.
144*65c59e02SInna Palant   bool thisAttached_;
145*65c59e02SInna Palant 
146*65c59e02SInna Palant   // The thread local pointer may point here.
147*65c59e02SInna Palant   detail::TLData data_;
148*65c59e02SInna Palant };
149*65c59e02SInna Palant 
150*65c59e02SInna Palant } // namespace jni
151*65c59e02SInna Palant } // namespace facebook
152