Ocean
Loading...
Searching...
No Matches
ScopedJNILocalObject.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_PLATFORM_ANDROID_SCOPED_JNI_LOCAL_OBJECT_H
9#define META_OCEAN_PLATFORM_ANDROID_SCOPED_JNI_LOCAL_OBJECT_H
10
13
15
16/**
17 * Define OCEAN_ENABLE_JNI_REFERENCE_VERIFICATION to verify in a release build that a local reference is released on the thread which has created it.
18 * Debug builds verify this always, the macro exists for issues which cannot be reproduced in a debug build.
19 * @see ScopedJNILocalObject.
20 * @ingroup platformandroid
21 */
22#if defined(OCEAN_DEBUG) || defined(OCEAN_ENABLE_JNI_REFERENCE_VERIFICATION)
23 #define OCEAN_VERIFY_JNI_REFERENCE_THREAD
24#endif
25
26namespace Ocean
27{
28
29namespace Platform
30{
31
32namespace Android
33{
34
35// Forward declaration.
36template <typename T> class ScopedJNILocalObject;
37
38// Forward declaration.
39template <typename T> class ScopedJNIGlobalObject;
40
41/**
42 * Definition of a scoped object encapsulating a local reference to a jclass object.
43 * @see ScopedJNILocalObject
44 * @ingroup platformandroid
45 */
47
48/**
49 * Definition of a scoped object encapsulating a local reference to a jobject object.
50 * @see ScopedJNILocalObject
51 * @ingroup platformandroid
52 */
54
55/**
56 * Definition of a scoped object encapsulating a local reference to a jobjectArray object.
57 * @see ScopedJNILocalObject
58 * @ingroup platformandroid
59 */
61
62/**
63 * Definition of a scoped object encapsulating a local reference to a jstring object.
64 * @see ScopedJNILocalObject
65 * @ingroup platformandroid
66 */
68
69/**
70 * This class implements a scoped local reference to a JNI object.
71 * A local reference belongs to the thread which has created it and is valid until that thread returns to Java,
72 * therefore this object must be used as a short living function-local object only.
73 * Use ScopedJNIGlobalObject instead whenever the reference needs to be stored, or needs to be used on another thread.
74 * The following code example shows the intended usage of this object:
75 * @code
76 * void functionUsingJavaObjects(JNIEnv& jniEnv, jobjectArray javaArray, const jsize size)
77 * {
78 * // the reference is deleted when the scope ends, also if the function returns early
79 * const ScopedJClass javaClassList(jniEnv, jniEnv.FindClass("java/util/List"));
80 *
81 * if (!javaClassList)
82 * {
83 * return;
84 * }
85 *
86 * for (jsize n = 0; n < size; ++n)
87 * {
88 * // the reference is deleted at the end of each iteration, so that only one reference is alive at a time
89 * const ScopedJString javaString(jniEnv, jstring(jniEnv.GetObjectArrayElement(javaArray, n)));
90 * }
91 * }
92 *
93 * class MyClass
94 * {
95 * protected:
96 *
97 * // WRONG: a local reference must not be stored, use ScopedGlobalJClass instead
98 * ScopedJClass javaClass_;
99 * };
100 * @endcode
101 * @tparam T The data type of the JNI object e.g., jobject, jclass, jstring
102 * @see ScopedJNIGlobalObject.
103 * @ingroup platformandroid
104 */
105template <typename T>
107{
108 friend class ScopedJNIGlobalObject<T>;
109
110 public:
111
112 /**
113 * Default constructor.
114 */
116
117 /**
118 * Move constructor.
119 * @param object The object to be moved
120 */
122
123 /**
124 * Creates a new scoped object for a given local reference, the object takes over the ownership of the reference.
125 * @param jniEnvironment The JNI environment of the calling thread, must be valid
126 * @param object The local reference to be encapsulated, can be nullptr
127 */
128 inline ScopedJNILocalObject(JNIEnv& jniEnvironment, T object);
129
130 /**
131 * Creates a new scoped object for a given local reference, the object takes over the ownership of the reference.
132 * Beware: This object must not live longer than the provided scoped JNI environment!
133 * @param scopedJNIEnvironment The associated scoped JNI environment
134 * @param object The local reference to be encapsulated, can be nullptr
135 */
136 inline ScopedJNILocalObject(const ScopedJNIEnvironment& scopedJNIEnvironment, T object);
137
138 /**
139 * Destructs this scoped object and deletes the local reference.
140 */
141 inline ~ScopedJNILocalObject();
142
143 /**
144 * Deletes the local reference and makes this object invalid.
145 * Beware: This function must be called on the thread which has created the reference.
146 */
147 inline void release();
148
149 /**
150 * Returns whether this scoped object holds a valid reference.
151 * @return True, if so
152 */
153 inline bool isValid() const;
154
155 /**
156 * Returns the encapsulated reference.
157 * @return The encapsulated reference, nullptr if no reference is encapsulated
158 */
159 inline const T& object() const;
160
161 /**
162 * Returns the encapsulated reference, must not be called if no reference is encapsulated.
163 * @see isValid().
164 * @return The encapsulated reference
165 */
166 inline const T& operator*() const;
167
168 /**
169 * Returns whether this scoped object holds a valid reference.
170 * @return True, if so
171 */
172 explicit inline operator bool() const;
173
174 /**
175 * Move operator.
176 * @param object The object to be moved
177 * @return Reference to this object
178 */
180
181 protected:
182
183 /**
184 * Disables copy constructor.
185 * @param object The object which would be copied
186 */
188
189 /**
190 * Disables copy operator.
191 * @param object The object which would be copied
192 * @return Reference to this object
193 */
195
196 /**
197 * Verifies that the calling thread is still the thread which has created the reference.
198 * The verification is based on GetEnv() which is a lookup of thread-local state of the runtime, the function neither attaches the thread nor allocates.
199 * The function reports success without verifying anything if the verification is not enabled for this build.
200 * Beware: The verification compares the environment of the calling thread with the environment of the creating thread,
201 * so that a misuse stays undetected whenever the runtime has re-used the address of an environment which has been released in the meantime.
202 * @return True, if the reference can be deleted; False, if the reference does not exist anymore or belongs to another thread
203 */
204 inline bool verifyCreatingThread() const;
205
206 protected:
207
208 /// The JNI environment of the thread which has created the reference.
209 JNIEnv* jniEnvironment_ = nullptr;
210
211 /// The encapsulated local reference.
212 T object_ = T();
213
214 /// The Java virtual machine of the creating thread, nullptr if the verification is not enabled for this build.
215 /// The member exists in every build so that the size of this object does not depend on the verification, which would break the one definition rule.
216 JavaVM* verificationJavaVM_ = nullptr;
217};
218
219template <typename T>
221{
222 *this = std::move(object);
223}
224
225template <typename T>
226inline ScopedJNILocalObject<T>::ScopedJNILocalObject(JNIEnv& jniEnvironment, T object) :
227 jniEnvironment_(&jniEnvironment),
228 object_(object)
229{
230#ifdef OCEAN_VERIFY_JNI_REFERENCE_THREAD
231 if (object_ != nullptr)
232 {
233 jniEnvironment.GetJavaVM(&verificationJavaVM_);
234 }
235#endif // OCEAN_VERIFY_JNI_REFERENCE_THREAD
236}
237
238template <typename T>
239inline ScopedJNILocalObject<T>::ScopedJNILocalObject(const ScopedJNIEnvironment& scopedJNIEnvironment, T object)
240{
241 ocean_assert(scopedJNIEnvironment.isValid());
242
243 if (!scopedJNIEnvironment.isValid())
244 {
245 // without an environment the reference could never be deleted, so that this object must not take over the ownership
246 return;
247 }
248
249 *this = ScopedJNILocalObject<T>(*scopedJNIEnvironment.jniEnv(), object);
250}
251
252template <typename T>
254{
255 release();
256}
257
258template <typename T>
260{
261 if (object_ == nullptr)
262 {
263 jniEnvironment_ = nullptr;
264 return;
265 }
266
267 ocean_assert(jniEnvironment_ != nullptr);
268
269 if (verifyCreatingThread())
270 {
271 jniEnvironment_->DeleteLocalRef(object_);
272 }
273
274 jniEnvironment_ = nullptr;
275 object_ = T();
276 verificationJavaVM_ = nullptr;
277}
278
279template <typename T>
281{
282 if (verificationJavaVM_ == nullptr)
283 {
284 // the verification is not enabled for this build, or the virtual machine could not be determined
285 return true;
286 }
287
288 JNIEnv* callingJNIEnvironment = nullptr;
289
290 if (verificationJavaVM_->GetEnv((void**)(&callingJNIEnvironment), JNI_VERSION_1_6) != JNI_OK)
291 {
292 // the thread has been detached in the meantime, which has released the thread's local references already,
293 // deleting the reference would access the environment of a thread which does not exist anymore
294
295 Messenger::writeToDebugOutput("ScopedJNILocalObject: The thread has been detached, the local reference does not exist anymore!");
296 ocean_assert(false && "The thread has been detached, the local reference does not exist anymore!");
297
298 return false;
299 }
300
301 if (callingJNIEnvironment != jniEnvironment_)
302 {
303 // a local reference belongs to the thread which has created it, deleting it here would modify the reference table of the calling thread
304
305 Messenger::writeToDebugOutput("ScopedJNILocalObject: The local reference must be released on the thread which has created it!");
306 ocean_assert(false && "The local reference must be released on the thread which has created it!");
307
308 return false;
309 }
310
311 return true;
312}
313
314template <typename T>
316{
317 return object_ != nullptr;
318}
319
320template <typename T>
321inline const T& ScopedJNILocalObject<T>::object() const
322{
323 return object_;
324}
325
326template <typename T>
328{
329 ocean_assert(isValid());
330
331 return object_;
332}
333
334template <typename T>
336{
337 return isValid();
338}
339
340template <typename T>
342{
343 if (this != &object)
344 {
345 release();
346
347 jniEnvironment_ = object.jniEnvironment_;
348 object_ = object.object_;
349
350 verificationJavaVM_ = object.verificationJavaVM_;
351
352 object.jniEnvironment_ = nullptr;
353 object.object_ = T();
354 object.verificationJavaVM_ = nullptr;
355 }
356
357 return *this;
358}
359
360}
361
362}
363
364}
365
366#endif // META_OCEAN_PLATFORM_ANDROID_SCOPED_JNI_LOCAL_OBJECT_H
static void writeToDebugOutput(const std::string &message)
Writes a message to the most suitable debug output of the current platform.
This class implements a scoped access to a JNI environment with attached current thread.
Definition ScopedJNIEnvironment.h:28
bool isValid() const
Returns whether this scoped object holds a valid JNI environment.
Definition ScopedJNIEnvironment.h:98
JNIEnv * jniEnv() const
Returns the JNI environment which is attached with the current thread.
Definition ScopedJNIEnvironment.h:93
This class implements a scoped global reference to a JNI object.
Definition ScopedJNIGlobalObject.h:98
This class implements a scoped local reference to a JNI object.
Definition ScopedJNILocalObject.h:107
ScopedJNILocalObject(const ScopedJNILocalObject &object)=delete
Disables copy constructor.
~ScopedJNILocalObject()
Destructs this scoped object and deletes the local reference.
Definition ScopedJNILocalObject.h:253
const T & operator*() const
Returns the encapsulated reference, must not be called if no reference is encapsulated.
Definition ScopedJNILocalObject.h:327
bool verifyCreatingThread() const
Verifies that the calling thread is still the thread which has created the reference.
Definition ScopedJNILocalObject.h:280
T object_
The encapsulated local reference.
Definition ScopedJNILocalObject.h:212
JNIEnv * jniEnvironment_
The JNI environment of the thread which has created the reference.
Definition ScopedJNILocalObject.h:209
ScopedJNILocalObject & operator=(const ScopedJNILocalObject &object)=delete
Disables copy operator.
void release()
Deletes the local reference and makes this object invalid.
Definition ScopedJNILocalObject.h:259
const T & object() const
Returns the encapsulated reference.
Definition ScopedJNILocalObject.h:321
bool isValid() const
Returns whether this scoped object holds a valid reference.
Definition ScopedJNILocalObject.h:315
ScopedJNILocalObject< T > & operator=(ScopedJNILocalObject< T > &&object) noexcept
Move operator.
Definition ScopedJNILocalObject.h:341
JavaVM * verificationJavaVM_
The Java virtual machine of the creating thread, nullptr if the verification is not enabled for this ...
Definition ScopedJNILocalObject.h:216
ScopedJNILocalObject()=default
Default constructor.
The namespace covering the entire Ocean framework.
Definition Accessor.h:15