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