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