Skip to content

Commit 350c507

Browse files
committed
Add deleters that attach or get the JNIEnv
These are useful when the deletion will happen on an auxiliary thread, such as the finalizer thread.
1 parent aa08c90 commit 350c507

File tree

8 files changed

+185
-84
lines changed

8 files changed

+185
-84
lines changed

include/jni/advanced_ownership.hpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#pragma once
2+
3+
#include <jni/functions.hpp>
4+
5+
namespace jni
6+
{
7+
// A deleter that gets the JNIEnv via GetEnv, rather than storing the value passed to the constructor.
8+
// The deleting thread must have a JVM attachment.
9+
//
10+
// Useful when deletion will happen on an auxiliary thread, particularly the finalizer thread. In such
11+
// cases, you may use one of the following:
12+
//
13+
// low-level: UniqueGlobalRef<jobject, EnvGettingDeleter> and NewGlobalRef<EnvGettingDeleter>
14+
// high-level: Global<Object<Tag>, EnvGettingDeleter> and obj.NewGlobalRef<EnvGettingDeleter>
15+
//
16+
template < RefDeletionMethod DeleteRef >
17+
class EnvGettingDeleter
18+
{
19+
private:
20+
JavaVM* vm = nullptr;
21+
22+
public:
23+
EnvGettingDeleter() = default;
24+
EnvGettingDeleter(JNIEnv& e) : vm(&GetJavaVM(e)) {}
25+
26+
void operator()(jobject* p) const
27+
{
28+
if (p)
29+
{
30+
assert(vm);
31+
(GetEnv(*vm).*DeleteRef)(Unwrap(p));
32+
}
33+
}
34+
};
35+
36+
// A deleter that first tries GetEnv, but falls back to AttachCurrentThread if a JVM is not already attached.
37+
// In the latter case, it detaches after deleting the reference.
38+
//
39+
// Useful when deletion will happen on an auxiliary thread which may or may not have a JVM attachment. In such
40+
// cases, you may use one of the following:
41+
//
42+
// low-level: UniqueGlobalRef<jobject, EnvAttachingDeleter> and NewGlobalRef<EnvAttachingDeleter>
43+
// high-level: Global<Object<Tag>, EnvAttachingDeleter> and obj.NewGlobalRef<EnvAttachingDeleter>
44+
//
45+
template < RefDeletionMethod DeleteRef >
46+
class EnvAttachingDeleter
47+
{
48+
private:
49+
JavaVM* vm = nullptr;
50+
51+
public:
52+
EnvAttachingDeleter() = default;
53+
EnvAttachingDeleter(JNIEnv& e) : vm(&GetJavaVM(e)) {}
54+
55+
void operator()(jobject* p) const
56+
{
57+
if (p)
58+
{
59+
assert(vm);
60+
JNIEnv* env = nullptr;
61+
jint err = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_1);
62+
if (err == JNI_OK)
63+
{
64+
(env->*DeleteRef)(Unwrap(p));
65+
}
66+
else if (err == JNI_EDETACHED)
67+
{
68+
((*AttachCurrentThread(*vm)).*DeleteRef)(Unwrap(p));
69+
}
70+
else
71+
{
72+
CheckErrorCode(err);
73+
}
74+
}
75+
}
76+
};
77+
}

include/jni/array.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ namespace jni
8787
return Array<E>(&NewArray<E>(env, length));
8888
}
8989

90-
Global<Array<E>> NewGlobalRef(JNIEnv& env) const
90+
template < template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
91+
Global<Array<E>, Deleter> NewGlobalRef(JNIEnv& env) const
9192
{
92-
return SeizeGlobal(env, Array(jni::NewGlobalRef(env, array).release()));
93+
return SeizeGlobal<Deleter>(env, Array(jni::NewGlobalRef(env, array).release()));
9394
}
9495
};
9596

@@ -159,9 +160,10 @@ namespace jni
159160
return Array<Object<TheTag>>(&NewObjectArray(env, length, Class<TheTag>::Singleton(env), initialElement.Get()));
160161
}
161162

162-
Global<Array<Object<TheTag>>> NewGlobalRef(JNIEnv& env) const
163+
template < template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
164+
Global<Array<Object<TheTag>>, Deleter> NewGlobalRef(JNIEnv& env) const
163165
{
164-
return SeizeGlobal(env, Array(jni::NewGlobalRef(env, array).release()));
166+
return SeizeGlobal<Deleter>(env, Array(jni::NewGlobalRef(env, array).release()));
165167
}
166168
};
167169

include/jni/class.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,10 @@ namespace jni
147147
return StaticMethod<TagType, T>(env, *this, name);
148148
}
149149

150-
Global<Class<TagType>> NewGlobalRef(JNIEnv& env) const
150+
template < template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
151+
Global<Class<TagType>, Deleter> NewGlobalRef(JNIEnv& env) const
151152
{
152-
return SeizeGlobal(env, Class(*jni::NewGlobalRef(env, clazz).release()));
153+
return SeizeGlobal<Deleter>(env, Class(*jni::NewGlobalRef(env, clazz).release()));
153154
}
154155
};
155156
}

include/jni/functions.hpp

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -128,27 +128,40 @@ namespace jni
128128
}
129129

130130

131-
template < class T >
132-
UniqueGlobalRef<T> NewGlobalRef(JNIEnv& env, T* t)
131+
template < template < RefDeletionMethod > class Deleter, class T >
132+
UniqueGlobalRef<T, Deleter> NewGlobalRef(JNIEnv& env, T* t)
133133
{
134134
jobject* obj = Wrap<jobject*>(env.NewGlobalRef(Unwrap(t)));
135135
CheckJavaException(env);
136136
if (t && !obj)
137137
throw std::bad_alloc();
138-
return UniqueGlobalRef<T>(reinterpret_cast<T*>(obj), GlobalRefDeleter(env));
138+
return UniqueGlobalRef<T, Deleter>(reinterpret_cast<T*>(obj), Deleter<&JNIEnv::DeleteGlobalRef>(env));
139+
}
140+
141+
template < class T >
142+
UniqueGlobalRef<T> NewGlobalRef(JNIEnv& env, T* t)
143+
{
144+
return NewGlobalRef<DefaultRefDeleter>(env, t);
139145
}
140146

141147
// Attempt to promote a weak reference to a strong one. Returns an empty result
142148
// if the weak reference has expired.
143-
template < class T >
144-
UniqueGlobalRef<T> NewGlobalRef(JNIEnv& env, const UniqueWeakGlobalRef<T>& t)
149+
template < template < RefDeletionMethod > class Deleter, class T, template < RefDeletionMethod > class WeakDeleter >
150+
UniqueGlobalRef<T, Deleter> NewGlobalRef(JNIEnv& env, const UniqueWeakGlobalRef<T, WeakDeleter>& t)
145151
{
146152
jobject* obj = Wrap<jobject*>(env.NewGlobalRef(Unwrap(t)));
147153
CheckJavaException(env);
148-
return UniqueGlobalRef<T>(reinterpret_cast<T*>(obj), GlobalRefDeleter(env));
154+
return UniqueGlobalRef<T, Deleter>(reinterpret_cast<T*>(obj), Deleter<&JNIEnv::DeleteGlobalRef>(env));
155+
}
156+
157+
template < class T, template < RefDeletionMethod > class WeakDeleter >
158+
UniqueGlobalRef<T> NewGlobalRef(JNIEnv& env, const UniqueWeakGlobalRef<T, WeakDeleter>& t)
159+
{
160+
return NewGlobalRef<DefaultRefDeleter>(env, t);
149161
}
150162

151-
inline void DeleteGlobalRef(JNIEnv& env, UniqueGlobalRef<jobject>&& ref)
163+
template < class T, template < RefDeletionMethod > class Deleter >
164+
void DeleteGlobalRef(JNIEnv& env, UniqueGlobalRef<T, Deleter>&& ref)
152165
{
153166
env.DeleteGlobalRef(Unwrap(ref.release()));
154167
CheckJavaException(env);
@@ -162,20 +175,21 @@ namespace jni
162175
CheckJavaException(env);
163176
if (t && !obj)
164177
throw std::bad_alloc();
165-
return UniqueLocalRef<T>(reinterpret_cast<T*>(obj), LocalRefDeleter(env));
178+
return UniqueLocalRef<T>(reinterpret_cast<T*>(obj), DefaultRefDeleter<&JNIEnv::DeleteLocalRef>(env));
166179
}
167180

168181
// Attempt to promote a weak reference to a strong one. Returns an empty result
169182
// if the weak reference has expired.
170-
template < class T >
171-
UniqueLocalRef<T> NewLocalRef(JNIEnv& env, const UniqueWeakGlobalRef<T>& t)
183+
template < class T, template < RefDeletionMethod > class WeakDeleter >
184+
UniqueLocalRef<T> NewLocalRef(JNIEnv& env, const UniqueWeakGlobalRef<T, WeakDeleter>& t)
172185
{
173186
jobject* obj = Wrap<jobject*>(env.NewLocalRef(Unwrap(t)));
174187
CheckJavaException(env);
175-
return UniqueLocalRef<T>(reinterpret_cast<T*>(obj), LocalRefDeleter(env));
188+
return UniqueLocalRef<T>(reinterpret_cast<T*>(obj), DefaultRefDeleter<&JNIEnv::DeleteLocalRef>(env));
176189
}
177190

178-
inline void DeleteLocalRef(JNIEnv& env, UniqueLocalRef<jobject>&& ref)
191+
template < class T >
192+
void DeleteLocalRef(JNIEnv& env, UniqueLocalRef<T>&& ref)
179193
{
180194
env.DeleteLocalRef(Unwrap(ref.release()));
181195
CheckJavaException(env);
@@ -186,17 +200,25 @@ namespace jni
186200
CheckJavaExceptionThenErrorCode(env, env.EnsureLocalCapacity(capacity));
187201
}
188202

189-
template < class T >
190-
UniqueWeakGlobalRef<T> NewWeakGlobalRef(JNIEnv& env, T* t)
203+
204+
template < template < RefDeletionMethod > class Deleter, class T >
205+
UniqueWeakGlobalRef<T, Deleter> NewWeakGlobalRef(JNIEnv& env, T* t)
191206
{
192207
jobject* obj = Wrap<jobject*>(env.NewWeakGlobalRef(Unwrap(t)));
193208
CheckJavaException(env);
194209
if (t && !obj)
195210
throw std::bad_alloc();
196-
return UniqueWeakGlobalRef<T>(reinterpret_cast<T*>(obj), WeakGlobalRefDeleter(env));
211+
return UniqueWeakGlobalRef<T, Deleter>(reinterpret_cast<T*>(obj), Deleter<&JNIEnv::DeleteWeakGlobalRef>(env));
212+
}
213+
214+
template < class T >
215+
UniqueWeakGlobalRef<T> NewWeakGlobalRef(JNIEnv& env, T* t)
216+
{
217+
return NewWeakGlobalRef<DefaultRefDeleter>(env, t);
197218
}
198219

199-
inline void DeleteWeakGlobalRef(JNIEnv& env, UniqueWeakGlobalRef<jobject>&& ref)
220+
template < class T, template < RefDeletionMethod > class Deleter >
221+
void DeleteWeakGlobalRef(JNIEnv& env, UniqueWeakGlobalRef<T, Deleter>&& ref)
200222
{
201223
env.DeleteWeakGlobalRef(Unwrap(ref.release()));
202224
CheckJavaException(env);

include/jni/jni.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
#include <jni/static_field.hpp>
2121
#include <jni/native_method.hpp>
2222
#include <jni/boxing.hpp>
23+
#include <jni/advanced_ownership.hpp>

include/jni/object.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,16 @@ namespace jni
132132
CallNonvirtualMethod<void>(env, obj, clazz, method, Untag(args)...);
133133
}
134134

135-
Global<Object<TagType>> NewGlobalRef(JNIEnv& env) const
135+
template < template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
136+
Global<Object<TagType>, Deleter> NewGlobalRef(JNIEnv& env) const
136137
{
137-
return SeizeGlobal(env, Object(jni::NewGlobalRef(env, obj).release()));
138+
return SeizeGlobal<Deleter>(env, Object(jni::NewGlobalRef(env, obj).release()));
138139
}
139140

140-
Weak<Object<TagType>> NewWeakGlobalRef(JNIEnv& env) const
141+
template < template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
142+
Weak<Object<TagType>, Deleter> NewWeakGlobalRef(JNIEnv& env) const
141143
{
142-
return SeizeWeak(env, Object(jni::NewWeakGlobalRef(env, obj).release()));
144+
return SeizeWeak<Deleter>(env, Object(jni::NewWeakGlobalRef(env, obj).release()));
143145
}
144146

145147
Local<Object<TagType>> NewLocalRef(JNIEnv& env) const

include/jni/ownership.hpp

Lines changed: 13 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,73 +20,39 @@ namespace jni
2020
using UniqueLocalFrame = std::unique_ptr< JNIEnv, LocalFrameDeleter >;
2121

2222

23-
class GlobalRefDeleter
24-
{
25-
private:
26-
JNIEnv* env = nullptr;
27-
28-
public:
29-
GlobalRefDeleter() = default;
30-
GlobalRefDeleter(JNIEnv& e) : env(&e) {}
31-
32-
void operator()(jobject* p) const
33-
{
34-
if (p)
35-
{
36-
assert(env);
37-
env->DeleteGlobalRef(Unwrap(p));
38-
}
39-
}
40-
};
41-
42-
template < class T >
43-
using UniqueGlobalRef = std::unique_ptr< T, GlobalRefDeleter >;
23+
using RefDeletionMethod = void (JNIEnv::*)(::jobject);
4424

45-
46-
class WeakGlobalRefDeleter
25+
template < RefDeletionMethod DeleteRef >
26+
class DefaultRefDeleter
4727
{
4828
private:
4929
JNIEnv* env = nullptr;
5030

5131
public:
52-
WeakGlobalRefDeleter() = default;
53-
WeakGlobalRefDeleter(JNIEnv& e) : env(&e) {}
32+
DefaultRefDeleter() = default;
33+
DefaultRefDeleter(JNIEnv& e) : env(&e) {}
5434

5535
void operator()(jobject* p) const
5636
{
5737
if (p)
5838
{
5939
assert(env);
60-
env->DeleteWeakGlobalRef(Unwrap(p));
40+
(env->*DeleteRef)(Unwrap(p));
6141
}
6242
}
6343
};
6444

65-
template < class T >
66-
using UniqueWeakGlobalRef = std::unique_ptr< T, WeakGlobalRefDeleter >;
67-
68-
69-
class LocalRefDeleter
70-
{
71-
private:
72-
JNIEnv* env = nullptr;
7345

74-
public:
75-
LocalRefDeleter() = default;
76-
LocalRefDeleter(JNIEnv& e) : env(&e) {}
46+
template < class T, template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
47+
using UniqueGlobalRef = std::unique_ptr< T, Deleter<&JNIEnv::DeleteGlobalRef> >;
7748

78-
void operator()(jobject* p) const
79-
{
80-
if (p)
81-
{
82-
assert(env);
83-
env->DeleteLocalRef(Unwrap(p));
84-
}
85-
}
86-
};
49+
template < class T, template < RefDeletionMethod > class Deleter = DefaultRefDeleter >
50+
using UniqueWeakGlobalRef = std::unique_ptr< T, Deleter<&JNIEnv::DeleteWeakGlobalRef> >;
8751

52+
// Not parameterized by Deleter because local references should be short-lived enough
53+
// that DefaultRefDeleter suffices in all cases.
8854
template < class T >
89-
using UniqueLocalRef = std::unique_ptr< T, LocalRefDeleter >;
55+
using UniqueLocalRef = std::unique_ptr< T, DefaultRefDeleter<&JNIEnv::DeleteLocalRef> >;
9056

9157

9258
class StringCharsDeleter

0 commit comments

Comments
 (0)