Brian Carlstrom | dd8af23 | 2012-05-13 23:56:07 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 The Android Open Source Project |
| 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 | |
Ruben Brunk | a77f3a2 | 2013-09-09 02:21:31 -0700 | [diff] [blame] | 17 | #define LOG_TAG "JniConstants" |
Ruben Brunk | a77f3a2 | 2013-09-09 02:21:31 -0700 | [diff] [blame] | 18 | #include "ALog-priv.h" |
Brian Carlstrom | dd8af23 | 2012-05-13 23:56:07 -0700 | [diff] [blame] | 19 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 20 | #include "JniConstants.h" |
Brian Carlstrom | dd8af23 | 2012-05-13 23:56:07 -0700 | [diff] [blame] | 21 | |
Dimitry Ivanov | 13803f6 | 2016-03-21 13:37:12 -0700 | [diff] [blame] | 22 | #include <atomic> |
| 23 | #include <mutex> |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 24 | #include <string> |
Dimitry Ivanov | 13803f6 | 2016-03-21 13:37:12 -0700 | [diff] [blame] | 25 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 26 | #include "nativehelper/ScopedLocalRef.h" |
Dimitry Ivanov | 13803f6 | 2016-03-21 13:37:12 -0700 | [diff] [blame] | 27 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 28 | namespace { |
Brian Carlstrom | dd8af23 | 2012-05-13 23:56:07 -0700 | [diff] [blame] | 29 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 30 | // Mutex protecting the initialization of cached class references. |
| 31 | std::mutex g_class_refs_mutex; |
| 32 | |
| 33 | // Atomic boolean flag for double locked checking that class references are |
| 34 | // initialized before use. |
| 35 | std::atomic<bool> g_class_refs_initialized(false); |
| 36 | |
| 37 | // Cached global references to class instances. |
| 38 | // |
| 39 | // These are GC heap references that are initialized under the protection of |
| 40 | // |g_class_refs_mutex| as they should only be initialized once to avoid losing a |
| 41 | // global reference. Initialization happens lazily when an accessor tries to |
| 42 | // retrieve one of these classes. |
| 43 | |
| 44 | jclass g_file_descriptor_class = nullptr; // java.io.FileDescriptor |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 45 | jclass g_nio_access_class = nullptr; // java.nio.Access |
| 46 | jclass g_nio_buffer_class = nullptr; // java.nio.Buffer |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 47 | jclass g_reference_class = nullptr; // java.lang.ref.Reference |
| 48 | jclass g_string_class = nullptr; // java.lang.String |
| 49 | |
| 50 | // Cached field and method ids. |
| 51 | // |
| 52 | // These are non-GC heap values. They are initialized lazily and racily. We |
| 53 | // avoid holding a mutex here because the JNI API supports concurrent calls to |
| 54 | // Get{Field,Method}ID and also because finding an id may recursively call into |
| 55 | // Get{Field,Method}ID. |
| 56 | // |
| 57 | // The recursion issue occurs here for the fields in the FileDescriptor class |
| 58 | // since retrieving a field id requires the class to be initialized. Class |
| 59 | // initialization leads to the initialization of static fields. The |
| 60 | // FileDescriptor class has static fields that are FileDescriptor instances. The |
| 61 | // initialization of these static FileDescriptor fields follows a convoluted |
| 62 | // path that that leads to a call to jniGetFDFromFileDescriptor() which then |
| 63 | // needs to call GetFieldID() which is in the call stack. If thread-safety were |
| 64 | // desirable here, a recursive mutex would be required. |
| 65 | // |
| 66 | // These field and method ids have default values of nullptr. They are reset |
| 67 | // back to nullptr in JniConstants::Uninitialize(), along with the class |
| 68 | // references, when a new runtime instance is created via JNI_CreateJavaVM(). The |
| 69 | // reset happens before the new runtime instance is returned to the caller and |
| 70 | // under the protection of the |g_class_refs_mutex|. |
| 71 | |
| 72 | jfieldID g_file_descriptor_descriptor_field = nullptr; // java.io.FileDescriptor.descriptor |
| 73 | jfieldID g_file_descriptor_owner_id_field = nullptr; // java.io.FileDescriptor.ownerId |
| 74 | jmethodID g_file_descriptor_init_method = nullptr; // void java.io.FileDescriptor.<init>() |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 75 | jmethodID g_nio_access_get_base_array_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() |
| 76 | jmethodID g_nio_access_get_base_array_offset_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() |
| 77 | jfieldID g_nio_buffer_address_field = nullptr; // long java.nio.Buffer.address |
| 78 | jfieldID g_nio_buffer_element_size_shift_field = nullptr; // int java.nio.Buffer._elementSizeShift |
| 79 | jfieldID g_nio_buffer_limit_field = nullptr; // int java.nio.Buffer.limit |
| 80 | jfieldID g_nio_buffer_position_field = nullptr; // int java.nio.Buffer.position |
| 81 | jmethodID g_nio_buffer_array_method = nullptr; // Object java.nio.Buffer.array() |
| 82 | jmethodID g_nio_buffer_array_offset_method = nullptr; // int java.nio.Buffer.arrayOffset() |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 83 | jmethodID g_reference_get_method = nullptr; // Object java.lang.ref.Reference.get() |
| 84 | |
| 85 | jclass FindClass(JNIEnv* env, const char* name) { |
| 86 | ScopedLocalRef<jclass> klass(env, env->FindClass(name)); |
| 87 | ALOG_ALWAYS_FATAL_IF(klass.get() == nullptr, "failed to find class '%s'", name); |
| 88 | return reinterpret_cast<jclass>(env->NewGlobalRef(klass.get())); |
| 89 | } |
| 90 | |
| 91 | jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) { |
| 92 | jfieldID result = env->GetFieldID(klass, name, desc); |
| 93 | ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find field '%s:%s'", name, desc); |
Brian Carlstrom | dd8af23 | 2012-05-13 23:56:07 -0700 | [diff] [blame] | 94 | return result; |
| 95 | } |
| 96 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 97 | jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { |
| 98 | jmethodID result = env->GetMethodID(klass, name, signature); |
| 99 | ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find method '%s%s'", name, signature); |
| 100 | return result; |
Brian Carlstrom | dd8af23 | 2012-05-13 23:56:07 -0700 | [diff] [blame] | 101 | } |
Logan Chien | 63e4917 | 2016-06-08 11:33:36 +0800 | [diff] [blame] | 102 | |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 103 | jmethodID FindStaticMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { |
| 104 | jmethodID result = env->GetStaticMethodID(klass, name, signature); |
| 105 | ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find static method '%s%s'", name, signature); |
| 106 | return result; |
Logan Chien | 63e4917 | 2016-06-08 11:33:36 +0800 | [diff] [blame] | 107 | } |
| 108 | |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 109 | } // namespace |
| 110 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 111 | jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) { |
| 112 | EnsureClassReferencesInitialized(env); |
| 113 | return g_file_descriptor_class; |
| 114 | } |
| 115 | |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 116 | jclass JniConstants::GetNioAccessClass(JNIEnv* env) { |
| 117 | EnsureClassReferencesInitialized(env); |
| 118 | return g_nio_access_class; |
| 119 | } |
| 120 | |
| 121 | jclass JniConstants::GetNioBufferClass(JNIEnv* env) { |
| 122 | EnsureClassReferencesInitialized(env); |
| 123 | return g_nio_buffer_class; |
| 124 | } |
| 125 | |
| 126 | jclass JniConstants::GetReferenceClass(JNIEnv* env) { |
| 127 | EnsureClassReferencesInitialized(env); |
| 128 | return g_reference_class; |
| 129 | } |
| 130 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 131 | jclass JniConstants::GetStringClass(JNIEnv* env) { |
| 132 | EnsureClassReferencesInitialized(env); |
| 133 | return g_string_class; |
| 134 | } |
| 135 | |
| 136 | jfieldID JniConstants::GetFileDescriptorDescriptorField(JNIEnv* env) { |
| 137 | if (g_file_descriptor_descriptor_field == nullptr) { |
| 138 | jclass klass = GetFileDescriptorClass(env); |
| 139 | g_file_descriptor_descriptor_field = FindField(env, klass, "descriptor", "I"); |
| 140 | } |
| 141 | return g_file_descriptor_descriptor_field; |
| 142 | } |
| 143 | |
| 144 | jfieldID JniConstants::GetFileDescriptorOwnerIdField(JNIEnv* env) { |
| 145 | if (g_file_descriptor_owner_id_field == nullptr) { |
| 146 | jclass klass = GetFileDescriptorClass(env); |
| 147 | g_file_descriptor_owner_id_field = FindField(env, klass, "ownerId", "J"); |
| 148 | } |
| 149 | return g_file_descriptor_owner_id_field; |
| 150 | } |
| 151 | |
| 152 | jmethodID JniConstants::GetFileDescriptorInitMethod(JNIEnv* env) { |
| 153 | if (g_file_descriptor_init_method == nullptr) { |
| 154 | jclass klass = GetFileDescriptorClass(env); |
| 155 | g_file_descriptor_init_method = FindMethod(env, klass, "<init>", "()V"); |
| 156 | } |
| 157 | return g_file_descriptor_init_method; |
| 158 | } |
| 159 | |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 160 | jmethodID JniConstants::GetNioAccessGetBaseArrayMethod(JNIEnv* env) { |
| 161 | if (g_nio_access_get_base_array_method == nullptr) { |
| 162 | jclass klass = GetNioAccessClass(env); |
| 163 | g_nio_access_get_base_array_method = |
| 164 | FindStaticMethod(env, klass, "getBaseArray", |
| 165 | "(Ljava/nio/Buffer;)Ljava/lang/Object;"); |
| 166 | } |
| 167 | return g_nio_access_get_base_array_method; |
| 168 | } |
| 169 | |
| 170 | jmethodID JniConstants::GetNioAccessGetBaseArrayOffsetMethod(JNIEnv* env) { |
| 171 | if (g_nio_access_get_base_array_offset_method == nullptr) { |
| 172 | jclass klass = GetNioAccessClass(env); |
| 173 | g_nio_access_get_base_array_offset_method = |
| 174 | FindStaticMethod(env, klass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); |
| 175 | } |
| 176 | return g_nio_access_get_base_array_offset_method; |
| 177 | } |
| 178 | |
| 179 | jfieldID JniConstants::GetNioBufferAddressField(JNIEnv* env) { |
| 180 | if (g_nio_buffer_address_field == nullptr) { |
| 181 | jclass klass = GetNioBufferClass(env); |
| 182 | g_nio_buffer_address_field = FindField(env, klass, "address", "J"); |
| 183 | } |
| 184 | return g_nio_buffer_address_field; |
| 185 | } |
| 186 | |
| 187 | jfieldID JniConstants::GetNioBufferElementSizeShiftField(JNIEnv* env) { |
| 188 | if (g_nio_buffer_element_size_shift_field == nullptr) { |
| 189 | jclass klass = GetNioBufferClass(env); |
| 190 | g_nio_buffer_element_size_shift_field = FindField(env, klass, "_elementSizeShift", "I"); |
| 191 | } |
| 192 | return g_nio_buffer_element_size_shift_field; |
| 193 | } |
| 194 | |
| 195 | jfieldID JniConstants::GetNioBufferLimitField(JNIEnv* env) { |
| 196 | if (g_nio_buffer_limit_field == nullptr) { |
| 197 | jclass klass = GetNioBufferClass(env); |
| 198 | g_nio_buffer_limit_field = FindField(env, klass, "limit", "I"); |
| 199 | } |
| 200 | return g_nio_buffer_limit_field; |
| 201 | } |
| 202 | |
| 203 | jfieldID JniConstants::GetNioBufferPositionField(JNIEnv* env) { |
| 204 | if (g_nio_buffer_position_field == nullptr) { |
| 205 | jclass klass = GetNioBufferClass(env); |
| 206 | g_nio_buffer_position_field = FindField(env, klass, "position", "I"); |
| 207 | } |
| 208 | return g_nio_buffer_position_field; |
| 209 | } |
| 210 | |
| 211 | jmethodID JniConstants::GetNioBufferArrayMethod(JNIEnv* env) { |
| 212 | if (g_nio_buffer_array_method == nullptr) { |
| 213 | jclass klass = GetNioBufferClass(env); |
| 214 | g_nio_buffer_array_method = FindMethod(env, klass, "array", "()Ljava/lang/Object;"); |
| 215 | } |
| 216 | return g_nio_buffer_array_method; |
| 217 | } |
| 218 | |
| 219 | jmethodID JniConstants::GetNioBufferArrayOffsetMethod(JNIEnv* env) { |
| 220 | if (g_nio_buffer_array_offset_method == nullptr) { |
| 221 | jclass klass = GetNioBufferClass(env); |
| 222 | g_nio_buffer_array_offset_method = FindMethod(env, klass, "arrayOffset", "()I"); |
| 223 | } |
| 224 | return g_nio_buffer_array_offset_method; |
| 225 | } |
| 226 | |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 227 | jmethodID JniConstants::GetReferenceGetMethod(JNIEnv* env) { |
| 228 | if (g_reference_get_method == nullptr) { |
| 229 | jclass klass = GetReferenceClass(env); |
| 230 | g_reference_get_method = FindMethod(env, klass, "get", "()Ljava/lang/Object;"); |
| 231 | } |
| 232 | return g_reference_get_method; |
| 233 | } |
| 234 | |
| 235 | void JniConstants::EnsureClassReferencesInitialized(JNIEnv* env) { |
| 236 | // Fast check if class references are initialized. |
| 237 | if (g_class_refs_initialized.load(std::memory_order_acquire)) { |
| 238 | return; |
| 239 | } |
| 240 | |
| 241 | // Slower check with initialization if necessary. |
| 242 | std::lock_guard<std::mutex> guard(g_class_refs_mutex); |
| 243 | if (g_class_refs_initialized.load(std::memory_order_relaxed)) { |
| 244 | return; |
| 245 | } |
| 246 | |
| 247 | // Class constants should be initialized only once because they global |
| 248 | // references. Field ids and Method ids can be initialized later since they |
| 249 | // are not references and races only have trivial performance |
| 250 | // consequences. |
| 251 | g_file_descriptor_class = FindClass(env, "java/io/FileDescriptor"); |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 252 | g_nio_access_class = FindClass(env, "java/nio/NIOAccess"); |
| 253 | g_nio_buffer_class = FindClass(env, "java/nio/Buffer"); |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 254 | g_reference_class = FindClass(env, "java/lang/ref/Reference"); |
| 255 | g_string_class = FindClass(env, "java/lang/String"); |
| 256 | g_class_refs_initialized.store(true, std::memory_order_release); |
| 257 | } |
| 258 | |
| 259 | void JniConstants::Uninitialize() { |
| 260 | // This method is called when a new runtime instance is created. There is no |
| 261 | // notification of a runtime instance being destroyed in the JNI interface |
| 262 | // so we piggyback on creation. Since only one runtime is supported at a |
| 263 | // time, we know the constants are invalid when JNI_CreateJavaVM() is |
| 264 | // called. |
| 265 | // |
| 266 | // Clean shutdown would require calling DeleteGlobalRef() for each of the |
| 267 | // class references. |
| 268 | std::lock_guard<std::mutex> guard(g_class_refs_mutex); |
| 269 | g_file_descriptor_class = nullptr; |
| 270 | g_file_descriptor_descriptor_field = nullptr; |
| 271 | g_file_descriptor_owner_id_field = nullptr; |
| 272 | g_file_descriptor_init_method = nullptr; |
Orion Hodson | bcc2b6f | 2019-02-22 16:41:10 +0000 | [diff] [blame] | 273 | g_nio_access_class = nullptr; |
| 274 | g_nio_access_get_base_array_method = nullptr; |
| 275 | g_nio_access_get_base_array_offset_method = nullptr; |
| 276 | g_nio_buffer_class = nullptr; |
| 277 | g_nio_buffer_address_field = nullptr; |
| 278 | g_nio_buffer_element_size_shift_field = nullptr; |
| 279 | g_nio_buffer_limit_field = nullptr; |
| 280 | g_nio_buffer_position_field = nullptr; |
| 281 | g_nio_buffer_array_method = nullptr; |
| 282 | g_nio_buffer_array_offset_method = nullptr; |
Orion Hodson | aadb373 | 2018-11-21 12:58:34 +0000 | [diff] [blame] | 283 | g_reference_class = nullptr; |
| 284 | g_reference_get_method = nullptr; |
| 285 | g_string_class = nullptr; |
| 286 | g_class_refs_initialized.store(false, std::memory_order_release); |
Logan Chien | 63e4917 | 2016-06-08 11:33:36 +0800 | [diff] [blame] | 287 | } |