Ocean
Loading...
Searching...
No Matches
ScopedJNIGlobalObject.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_GLOBAL_OBJECT_H
9#define META_OCEAN_PLATFORM_ANDROID_SCOPED_JNI_GLOBAL_OBJECT_H
10
14
16
17namespace Ocean
18{
19
20namespace Platform
21{
22
23namespace Android
24{
25
26// Forward declaration.
27template <typename T> class ScopedJNIGlobalObject;
28
29/**
30 * Definition of a scoped object encapsulating a global reference to a jclass object.
31 * @see ScopedJNIGlobalObject
32 * @ingroup platformandroid
33 */
35
36/**
37 * Definition of a scoped object encapsulating a global reference to a jobject object.
38 * @see ScopedJNIGlobalObject
39 * @ingroup platformandroid
40 */
42
43/**
44 * Definition of a scoped object encapsulating a global reference to a jobjectArray object.
45 * @see ScopedJNIGlobalObject
46 * @ingroup platformandroid
47 */
49
50/**
51 * Definition of a scoped object encapsulating a global reference to a jstring object.
52 * @see ScopedJNIGlobalObject
53 * @ingroup platformandroid
54 */
56
57/**
58 * This class implements a scoped global reference to a JNI object.
59 * A global reference can be used on any thread and stays valid until it is released explicitly,
60 * therefore this object is the correct choice whenever a reference needs to be stored as a member.
61 * The object always creates and owns its own global reference.
62 * A local reference can be handed over, in which case it is deleted as soon as the global reference exists, any other reference stays owned by the caller.
63 * The following code example shows the intended usage of this object:
64 * @code
65 * class MyClass
66 * {
67 * public:
68 *
69 * bool initialize(JNIEnv& jniEnv);
70 *
71 * protected:
72 *
73 * // the global reference can be used on any thread, and is released when this object is disposed
74 * ScopedGlobalJClass javaClass_;
75 * };
76 *
77 * bool MyClass::initialize(JNIEnv& jniEnv)
78 * {
79 * ScopedJClass javaClass(jniEnv, jniEnv.FindClass("com/meta/ocean/Example"));
80 *
81 * if (!javaClass)
82 * {
83 * return false;
84 * }
85 *
86 * // the local reference is handed over, on success it is deleted and only the global reference remains
87 * javaClass_ = ScopedGlobalJClass(std::move(javaClass));
88 *
89 * return javaClass_.isValid();
90 * }
91 * @endcode
92 * @tparam T The data type of the JNI object e.g., jobject, jclass
93 * @see ScopedJNILocalObject.
94 * @ingroup platformandroid
95 */
96template <typename T>
98{
99 public:
100
101 /**
102 * Default constructor.
103 */
105
106 /**
107 * Move constructor.
108 * @param object The object to be moved
109 */
111
112 /**
113 * Creates a new global reference for a given local reference and takes over the ownership of that local reference.
114 * The local reference is deleted immediately, so that only the new global reference remains.
115 * This is the intended way to turn a local reference into a global one.
116 * The local reference is kept in case the global reference could not be created.
117 * @param localObject The local reference to be turned into a global reference, can be invalid
118 */
119 explicit inline ScopedJNIGlobalObject(ScopedJNILocalObject<T>&& localObject);
120
121 /**
122 * Creates a new global reference for a given reference, the caller keeps the ownership of the provided reference.
123 * The provided reference is not touched, as this object cannot know whether the reference is local, global, or owned by someone else.
124 * Beware: In case the provided reference is a local reference which the caller owns, the caller must still release it, so that the constructor taking a ScopedJNILocalObject is the better choice in that case.
125 * @param jniEnvironment The JNI environment of the calling thread, must be valid
126 * @param object The reference for which a new global reference will be created, can be nullptr
127 */
128 inline ScopedJNIGlobalObject(JNIEnv& jniEnvironment, T object);
129
130 /**
131 * Creates a new global reference for a given reference, the caller keeps the ownership of the provided reference.
132 * The provided reference is not touched, as this object cannot know whether the reference is local, global, or owned by someone else.
133 * @param scopedJNIEnvironment The associated scoped JNI environment
134 * @param object The reference for which a new global reference will be created, can be nullptr
135 */
136 inline ScopedJNIGlobalObject(const ScopedJNIEnvironment& scopedJNIEnvironment, T object);
137
138 /**
139 * Destructs this scoped object and deletes the global reference.
140 */
141 inline ~ScopedJNIGlobalObject();
142
143 /**
144 * Deletes the global reference and makes this object invalid.
145 * The reference can be deleted through the JNI environment of an attached thread only, so that this function must be called from a thread which is attached to the virtual machine.
146 * Beware: The reference is not deleted if the calling thread is not attached, the virtual machine reclaims it once it ends.
147 * This object does not attach the thread itself, as an attach allocates a Java thread object and can block until a running garbage collection has finished.
148 * A thread which releases global references regularly should be attached once for its entire lifetime, e.g., through NativeInterfaceManager::environment().
149 */
150 inline void release();
151
152 /**
153 * Returns whether the calling thread is attached to the virtual machine of this reference, and thus whether this object can be released on the calling thread.
154 * An object without a reference can always be released, as there is nothing to delete.
155 * Beware: A thread must not be attached just to release a reference, instead a thread which releases global references should be attached for its entire lifetime, e.g., through NativeInterfaceManager::environment().
156 * @return True, if so
157 */
158 inline bool isThreadAttached() const;
159
160 /**
161 * Returns whether this scoped object holds a valid reference.
162 * @return True, if so
163 */
164 inline bool isValid() const;
165
166 /**
167 * Returns the encapsulated reference.
168 * @return The encapsulated reference, nullptr if no reference is encapsulated
169 */
170 inline const T& object() const;
171
172 /**
173 * Returns the encapsulated reference, must not be called if no reference is encapsulated.
174 * @see isValid().
175 * @return The encapsulated reference
176 */
177 inline const T& operator*() const;
178
179 /**
180 * Returns whether this scoped object holds a valid reference.
181 * @return True, if so
182 */
183 explicit inline operator bool() const;
184
185 /**
186 * Move operator.
187 * @param object The object to be moved
188 * @return Reference to this object
189 */
191
192 protected:
193
194 /**
195 * Disables copy constructor.
196 * @param object The object which would be copied
197 */
199
200 /**
201 * Disables copy operator.
202 * @param object The object which would be copied
203 * @return Reference to this object
204 */
206
207 protected:
208
209 /// The Java virtual machine of the environment which has created the global reference.
210 JavaVM* javaVM_ = nullptr;
211
212 /// The encapsulated global reference.
213 T object_ = T();
214};
215
216template <typename T>
218{
219 *this = std::move(object);
220}
221
222template <typename T>
224{
225 if (localObject.object_ == nullptr)
226 {
227 return;
228 }
229
230 ocean_assert(localObject.jniEnvironment_ != nullptr);
231
232 if (localObject.jniEnvironment_ == nullptr)
233 {
234 return;
235 }
236
237 // the environment of the local reference belongs to the thread which has created it,
238 // using it on any other thread would corrupt the runtime state of that thread, so that the reference must not be promoted here
239
240 if (!localObject.verifyCreatingThread())
241 {
242 return;
243 }
244
245 *this = ScopedJNIGlobalObject<T>(*localObject.jniEnvironment_, localObject.object_);
246
247 if (object_ == nullptr)
248 {
249 // the local reference is kept, so that the caller can still use it, e.g., to create a diagnostic
250 return;
251 }
252
253 localObject.release();
254}
255
256template <typename T>
257inline ScopedJNIGlobalObject<T>::ScopedJNIGlobalObject(JNIEnv& jniEnvironment, T object)
258{
259 if (object == nullptr)
260 {
261 return;
262 }
263
264 if (jniEnvironment.GetJavaVM(&javaVM_) != JNI_OK)
265 {
266 ocean_assert(false && "Failed to determine the Java virtual machine!");
267
268 javaVM_ = nullptr;
269 return;
270 }
271
272 object_ = T(jniEnvironment.NewGlobalRef(object));
273
274 if (object_ == nullptr)
275 {
276 // a failing NewGlobalRef() may leave an OutOfMemoryError pending, the exception must not stay pending as the calling thread may never return to Java
277
278 if (jniEnvironment.ExceptionCheck() == JNI_TRUE)
279 {
280 jniEnvironment.ExceptionClear();
281 }
282
283 Log::error() << "ScopedJNIGlobalObject: Failed to create the global reference!";
284
285 javaVM_ = nullptr;
286 }
287}
288
289template <typename T>
291{
292 ocean_assert(scopedJNIEnvironment.isValid());
293
294 if (!scopedJNIEnvironment.isValid())
295 {
296 return;
297 }
298
299 *this = ScopedJNIGlobalObject<T>(*scopedJNIEnvironment.jniEnv(), object);
300}
301
302template <typename T>
304{
305 release();
306}
307
308template <typename T>
310{
311 if (object_ == nullptr)
312 {
313 return;
314 }
315
316 ocean_assert(javaVM_ != nullptr);
317
318 JNIEnv* jniEnvironment = nullptr;
319
320 if (javaVM_->GetEnv((void**)(&jniEnvironment), JNI_VERSION_1_6) == JNI_OK)
321 {
322 // the calling thread is attached to the virtual machine and owns the returned environment,
323 // so that the reference can be deleted right here, this is the expected case
324
325 jniEnvironment->DeleteGlobalRef(object_);
326 }
327 else
328 {
329 // the calling thread is not attached, the reference cannot be deleted without an environment,
330 // attaching the thread here would allocate a Java thread object and could block until a running garbage collection has finished,
331 // which is not acceptable in a destructor, so that the reference is abandoned and reclaimed once the virtual machine ends,
332 // a thread which releases global references can be attached for its entire lifetime with a call to NativeInterfaceManager::environment()
333
334 Messenger::writeToDebugOutput("ScopedJNIGlobalObject: The global reference is not deleted, the thread is not attached to the virtual machine!");
335
336 ocean_assert(false && "The global reference is leaked, release this object from a thread which is attached to the virtual machine!");
337 }
338
339 javaVM_ = nullptr;
340 object_ = T();
341}
342
343template <typename T>
345{
346 if (object_ == nullptr)
347 {
348 return true;
349 }
350
351 ocean_assert(javaVM_ != nullptr);
352
353 JNIEnv* jniEnvironment = nullptr;
354
355 return javaVM_->GetEnv((void**)(&jniEnvironment), JNI_VERSION_1_6) == JNI_OK;
356}
357
358template <typename T>
360{
361 return object_ != nullptr;
362}
363
364template <typename T>
365inline const T& ScopedJNIGlobalObject<T>::object() const
366{
367 return object_;
368}
369
370template <typename T>
372{
373 ocean_assert(isValid());
374
375 return object_;
376}
377
378template <typename T>
380{
381 return isValid();
382}
383
384template <typename T>
386{
387 if (this != &object)
388 {
389 release();
390
391 javaVM_ = object.javaVM_;
392 object_ = object.object_;
393
394 object.javaVM_ = nullptr;
395 object.object_ = T();
396 }
397
398 return *this;
399}
400
401}
402
403}
404
405}
406
407#endif // META_OCEAN_PLATFORM_ANDROID_SCOPED_JNI_GLOBAL_OBJECT_H
static MessageObject error()
Returns the message for error messages.
Definition Messenger.h:1095
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
ScopedJNIGlobalObject(const ScopedJNIGlobalObject &object)=delete
Disables copy constructor.
const T & operator*() const
Returns the encapsulated reference, must not be called if no reference is encapsulated.
Definition ScopedJNIGlobalObject.h:371
ScopedJNIGlobalObject()=default
Default constructor.
bool isThreadAttached() const
Returns whether the calling thread is attached to the virtual machine of this reference,...
Definition ScopedJNIGlobalObject.h:344
ScopedJNIGlobalObject< T > & operator=(ScopedJNIGlobalObject< T > &&object) noexcept
Move operator.
Definition ScopedJNIGlobalObject.h:385
const T & object() const
Returns the encapsulated reference.
Definition ScopedJNIGlobalObject.h:365
JavaVM * javaVM_
The Java virtual machine of the environment which has created the global reference.
Definition ScopedJNIGlobalObject.h:210
~ScopedJNIGlobalObject()
Destructs this scoped object and deletes the global reference.
Definition ScopedJNIGlobalObject.h:303
T object_
The encapsulated global reference.
Definition ScopedJNIGlobalObject.h:213
ScopedJNIGlobalObject & operator=(const ScopedJNIGlobalObject &object)=delete
Disables copy operator.
void release()
Deletes the global reference and makes this object invalid.
Definition ScopedJNIGlobalObject.h:309
bool isValid() const
Returns whether this scoped object holds a valid reference.
Definition ScopedJNIGlobalObject.h:359
This class implements a scoped local reference to a JNI object.
Definition ScopedJNILocalObject.h:107
ScopedJNIGlobalObject< jclass > ScopedGlobalJClass
Definition of a scoped object encapsulating a global reference to a jclass object.
Definition ScopedJNIGlobalObject.h:34
The namespace covering the entire Ocean framework.
Definition Accessor.h:15