blob: 6d6ce791ec3eb3015dcaa98c2cad19b5c3c44583 [file] [log] [blame]
Brian Carlstromdd8af232012-05-13 23:56:07 -07001/*
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 Brunka77f3a22013-09-09 02:21:31 -070017#define LOG_TAG "JniConstants"
Ruben Brunka77f3a22013-09-09 02:21:31 -070018#include "ALog-priv.h"
Brian Carlstromdd8af232012-05-13 23:56:07 -070019
Orion Hodsonaadb3732018-11-21 12:58:34 +000020#include "JniConstants.h"
Brian Carlstromdd8af232012-05-13 23:56:07 -070021
Dimitry Ivanov13803f62016-03-21 13:37:12 -070022#include <atomic>
23#include <mutex>
Orion Hodsonaadb3732018-11-21 12:58:34 +000024#include <string>
Dimitry Ivanov13803f62016-03-21 13:37:12 -070025
Orion Hodsonaadb3732018-11-21 12:58:34 +000026#include "nativehelper/ScopedLocalRef.h"
Dimitry Ivanov13803f62016-03-21 13:37:12 -070027
Orion Hodsonaadb3732018-11-21 12:58:34 +000028namespace {
Brian Carlstromdd8af232012-05-13 23:56:07 -070029
Orion Hodsonaadb3732018-11-21 12:58:34 +000030// Mutex protecting the initialization of cached class references.
31std::mutex g_class_refs_mutex;
32
33// Atomic boolean flag for double locked checking that class references are
34// initialized before use.
35std::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
44jclass g_file_descriptor_class = nullptr; // java.io.FileDescriptor
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +000045jclass g_nio_access_class = nullptr; // java.nio.Access
46jclass g_nio_buffer_class = nullptr; // java.nio.Buffer
Orion Hodsonaadb3732018-11-21 12:58:34 +000047jclass g_reference_class = nullptr; // java.lang.ref.Reference
48jclass 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
72jfieldID g_file_descriptor_descriptor_field = nullptr; // java.io.FileDescriptor.descriptor
73jfieldID g_file_descriptor_owner_id_field = nullptr; // java.io.FileDescriptor.ownerId
74jmethodID g_file_descriptor_init_method = nullptr; // void java.io.FileDescriptor.<init>()
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +000075jmethodID g_nio_access_get_base_array_method = nullptr; // Object java.nio.NIOAccess.getBaseArray()
76jmethodID g_nio_access_get_base_array_offset_method = nullptr; // Object java.nio.NIOAccess.getBaseArray()
77jfieldID g_nio_buffer_address_field = nullptr; // long java.nio.Buffer.address
78jfieldID g_nio_buffer_element_size_shift_field = nullptr; // int java.nio.Buffer._elementSizeShift
79jfieldID g_nio_buffer_limit_field = nullptr; // int java.nio.Buffer.limit
80jfieldID g_nio_buffer_position_field = nullptr; // int java.nio.Buffer.position
81jmethodID g_nio_buffer_array_method = nullptr; // Object java.nio.Buffer.array()
82jmethodID g_nio_buffer_array_offset_method = nullptr; // int java.nio.Buffer.arrayOffset()
Orion Hodsonaadb3732018-11-21 12:58:34 +000083jmethodID g_reference_get_method = nullptr; // Object java.lang.ref.Reference.get()
84
85jclass 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
91jfieldID 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 Carlstromdd8af232012-05-13 23:56:07 -070094 return result;
95}
96
Orion Hodsonaadb3732018-11-21 12:58:34 +000097jmethodID 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 Carlstromdd8af232012-05-13 23:56:07 -0700101}
Logan Chien63e49172016-06-08 11:33:36 +0800102
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000103jmethodID 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 Chien63e49172016-06-08 11:33:36 +0800107}
108
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000109} // namespace
110
Orion Hodsonaadb3732018-11-21 12:58:34 +0000111jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) {
112 EnsureClassReferencesInitialized(env);
113 return g_file_descriptor_class;
114}
115
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000116jclass JniConstants::GetNioAccessClass(JNIEnv* env) {
117 EnsureClassReferencesInitialized(env);
118 return g_nio_access_class;
119}
120
121jclass JniConstants::GetNioBufferClass(JNIEnv* env) {
122 EnsureClassReferencesInitialized(env);
123 return g_nio_buffer_class;
124}
125
126jclass JniConstants::GetReferenceClass(JNIEnv* env) {
127 EnsureClassReferencesInitialized(env);
128 return g_reference_class;
129}
130
Orion Hodsonaadb3732018-11-21 12:58:34 +0000131jclass JniConstants::GetStringClass(JNIEnv* env) {
132 EnsureClassReferencesInitialized(env);
133 return g_string_class;
134}
135
136jfieldID 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
144jfieldID 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
152jmethodID 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 Hodsonbcc2b6f2019-02-22 16:41:10 +0000160jmethodID 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
170jmethodID 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
179jfieldID 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
187jfieldID 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
195jfieldID 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
203jfieldID 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
211jmethodID 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
219jmethodID 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 Hodsonaadb3732018-11-21 12:58:34 +0000227jmethodID 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
235void 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 Hodsonbcc2b6f2019-02-22 16:41:10 +0000252 g_nio_access_class = FindClass(env, "java/nio/NIOAccess");
253 g_nio_buffer_class = FindClass(env, "java/nio/Buffer");
Orion Hodsonaadb3732018-11-21 12:58:34 +0000254 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
259void 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 Hodsonbcc2b6f2019-02-22 16:41:10 +0000273 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 Hodsonaadb3732018-11-21 12:58:34 +0000283 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 Chien63e49172016-06-08 11:33:36 +0800287}