Skip to content

Commit 4833f5e

Browse files
committed
Invalidate Callable when JVM is closing.
1 parent 872ed92 commit 4833f5e

File tree

5 files changed

+63
-15
lines changed

5 files changed

+63
-15
lines changed

src/jvm_wrapper/jvm_instance_wrapper.h

+28-10
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ public:
6868
template<class Derived, const char* FqName>
6969
class JvmInstanceWrapper {
7070
protected:
71-
bool is_weak = false;
71+
enum RefType {
72+
STRONG,
73+
WEAK,
74+
DEAD
75+
};
76+
77+
RefType ref_type = STRONG;
7278
jni::JObject wrapped;
7379

7480
explicit JvmInstanceWrapper(jni::Env& p_env, jni::JObject p_wrapped);
@@ -79,6 +85,7 @@ class JvmInstanceWrapper {
7985
const jni::JObject& get_wrapped() const;
8086
void swap_to_strong_unsafe(jni::Env& p_env);
8187
void swap_to_weak_unsafe(jni::Env& p_env);
88+
void invalidate_ref(jni::Env& p_env);
8289

8390
static bool initialize(jni::Env& p_env, ClassLoader* class_loader);
8491
static Derived* create_instance(jni::Env& p_env, ClassLoader* class_loader);
@@ -119,16 +126,12 @@ void JvmInstanceWrapper<Derived, FqName>::finalize(jni::Env& p_env, ClassLoader*
119126
template<class Derived, const char* FqName>
120127
JvmInstanceWrapper<Derived, FqName>::~JvmInstanceWrapper() {
121128
jni::Env env {jni::Jvm::current_env()};
122-
if (is_weak) {
123-
wrapped.delete_weak_ref(env);
124-
} else {
125-
wrapped.delete_global_ref(env);
126-
}
129+
invalidate_ref(env);
127130
}
128131

129132
template<class Derived, const char* FqName>
130133
bool JvmInstanceWrapper<Derived, FqName>::is_ref_weak() const {
131-
return is_weak;
134+
return ref_type == WEAK;
132135
}
133136

134137
template<class Derived, const char* FqName>
@@ -137,7 +140,7 @@ void JvmInstanceWrapper<Derived, FqName>::swap_to_strong_unsafe(jni::Env& p_env)
137140
jni::JObject new_ref = wrapped.new_global_ref<jni::JObject>(p_env);
138141
wrapped.delete_weak_ref(p_env);
139142
wrapped = new_ref;
140-
is_weak = false;
143+
ref_type = STRONG;
141144
}
142145

143146
template<class Derived, const char* FqName>
@@ -146,12 +149,27 @@ void JvmInstanceWrapper<Derived, FqName>::swap_to_weak_unsafe(jni::Env& p_env) {
146149
jni::JObject new_ref = wrapped.new_weak_ref<jni::JObject>(p_env);
147150
wrapped.delete_global_ref(p_env);
148151
wrapped = new_ref;
149-
is_weak = true;
152+
ref_type = WEAK;
153+
}
154+
155+
template<class Derived, const char* FqName>
156+
void JvmInstanceWrapper<Derived, FqName>::invalidate_ref(jni::Env& p_env) {
157+
switch (ref_type) {
158+
case STRONG:
159+
wrapped.delete_global_ref(p_env);
160+
break;
161+
case WEAK:
162+
wrapped.delete_weak_ref(p_env);
163+
break;
164+
default:
165+
break;
166+
}
167+
ref_type = DEAD;
150168
}
151169

152170
template<class Derived, const char* FqName>
153171
const jni::JObject& JvmInstanceWrapper<Derived, FqName>::get_wrapped() const {
154172
return wrapped;
155173
}
156174

157-
#endif// GODOT_JVM_JVM_INSTANCE_WRAPPER_H
175+
#endif // GODOT_JVM_JVM_INSTANCE_WRAPPER_H

src/jvm_wrapper/kotlin_callable_custom.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "kotlin_callable_custom.h"
22

33
#include "gd_kotlin.h"
4+
#include "jvm_wrapper/memory/memory_manager.h"
45
#include "jvm_wrapper/memory/transfer_context.h"
56

67
void LambdaContainer::invoke(jni::Env& p_env, const Variant** p_args, int args_count, Variant& r_ret) const {
@@ -82,4 +83,11 @@ bool JvmCallableCustom::compare_less(const CallableCustom* p_a, const CallableCu
8283
}
8384

8485
JvmCallableCustom::JvmCallableCustom(jni::Env& p_env, jni::JObject p_wrapped, Variant::Type return_type, int p_hash_code, bool p_has_on_destroy) :
85-
lambda(p_env, p_wrapped, return_type, p_hash_code, p_has_on_destroy) {}
86+
lambda(p_env, p_wrapped, return_type, p_hash_code, p_has_on_destroy) {
87+
MemoryManager::get_instance().add_callable(&self_list);
88+
}
89+
90+
void JvmCallableCustom::invalidate() {
91+
jni::Env env {jni::Jvm::current_env()};
92+
lambda.invalidate_ref(env);
93+
}

src/jvm_wrapper/kotlin_callable_custom.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#ifndef GODOT_JVM_KOTLIN_CALLABLE_CUSTOM_H
22
#define GODOT_JVM_KOTLIN_CALLABLE_CUSTOM_H
33

4-
#include "jvm_wrapper/jvm_instance_wrapper.h"
54
#include "core/variant/callable.h"
5+
#include "jvm_wrapper/jvm_instance_wrapper.h"
66

77
JVM_INSTANCE_WRAPPER(LambdaContainer, "godot.core.LambdaContainer") {
88
JVM_CLASS(LambdaContainer)
@@ -44,15 +44,16 @@ class JvmCallableCustom : public CallableCustom {
4444
CompareLessFunc get_compare_less_func() const override;
4545
ObjectID get_object() const override;
4646

47+
void invalidate();
48+
4749
JvmCallableCustom(jni::Env& p_env, jni::JObject p_wrapped, Variant::Type return_type, int p_hash_code, bool p_has_on_destroy);
48-
~JvmCallableCustom() = default;
4950

5051
private:
5152
LambdaContainer lambda;
53+
SelfList<JvmCallableCustom> self_list {this};
5254

5355
static bool compare_equal(const CallableCustom* p_a, const CallableCustom* p_b);
5456
static bool compare_less(const CallableCustom* p_a, const CallableCustom* p_b);
5557
};
5658

57-
58-
#endif //GODOT_JVM_KOTLIN_CALLABLE_CUSTOM_H
59+
#endif // GODOT_JVM_KOTLIN_CALLABLE_CUSTOM_H

src/jvm_wrapper/memory/memory_manager.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@ void MemoryManager::sync_memory(jni::Env& p_env) {
141141

142142
void MemoryManager::clean_up(jni::Env& p_env) {
143143
JVM_LOG_VERBOSE("Cleaning JVM Memory...");
144+
145+
//Iterate over all remaining custom callables and invalidate their JNI reference before the JVM shut down.
146+
SelfList<JvmCallableCustom> *it = callables.first();
147+
while (it) {
148+
JvmCallableCustom *obj = it->self();
149+
obj->invalidate();
150+
it = it->next();
151+
}
152+
callables.clear();
153+
144154
wrapped.call_void_method(p_env, CLEAN_UP);
145155
JVM_LOG_VERBOSE("JVM Memory cleaned!");
146156
}
@@ -175,5 +185,11 @@ void MemoryManager::direct_object_deletion(jni::Env& p_env, Object* p_obj) {
175185
memdelete(p_obj);
176186
}
177187

188+
void MemoryManager::add_callable(SelfList<JvmCallableCustom>* callable) {
189+
callable_mutex.lock();
190+
callables.add(callable);
191+
callable_mutex.unlock();
192+
}
193+
178194
MemoryManager::~MemoryManager() = default;
179195

src/jvm_wrapper/memory/memory_manager.h

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "jni/types.h"
55
#include "jvm_wrapper/jvm_singleton_wrapper.h"
6+
#include "jvm_wrapper/kotlin_callable_custom.h"
67
#include "jvm_wrapper/registration/kt_object.h"
78
#include "script/jvm_instance.h"
89

@@ -26,6 +27,9 @@ JVM_SINGLETON_WRAPPER(MemoryManager, "godot.internal.memory.MemoryManager") {
2627
INIT_NATIVE_METHOD("releaseBinding", "(J)V", MemoryManager::release_binding)
2728
)
2829

30+
Mutex callable_mutex;
31+
SelfList<JvmCallableCustom>::List callables;
32+
2933
Mutex dead_objects_mutex;
3034
LocalVector<ObjectID> dead_objects;
3135

@@ -39,6 +43,7 @@ JVM_SINGLETON_WRAPPER(MemoryManager, "godot.internal.memory.MemoryManager") {
3943

4044
public:
4145
void direct_object_deletion(jni::Env& p_env, Object* obj);
46+
void add_callable(SelfList<JvmCallableCustom>* callable);
4247
void queue_dead_object(Object* obj);
4348
void queue_demotion(JvmInstance* script_instance);
4449
void cancel_demotion(JvmInstance* script_instance);

0 commit comments

Comments
 (0)