Skip to content

Commit 00a8744

Browse files
Kotlin multiplatform leaking memory (#4037)
* Add deinit for KMP iOS and JVM targets * Add deinit for JS target * Add deinit for JS target * Fix JVM native name * Reuse one thread on JVM --------- Co-authored-by: satoshiotomakan <[email protected]>
1 parent b4221b4 commit 00a8744

File tree

5 files changed

+80
-0
lines changed

5 files changed

+80
-0
lines changed

codegen/lib/templates/kotlin/android_class.erb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ actual class <%= entity.name %> private constructor(
99

1010
init {
1111
if (nativeHandle == 0L) throw IllegalArgumentException()
12+
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
13+
GenericPhantomReference.register(this, nativeHandle, ::delete)
14+
<% end -%>
1215
}
1316
<%# Constructors -%>
1417
<%- constructors.each do |constructor| -%>
@@ -52,6 +55,12 @@ actual class <%= entity.name %> private constructor(
5255
@JvmStatic
5356
@JvmName("createFromNative")
5457
private fun createFromNative(nativeHandle: Long) = <%= entity.name %>(nativeHandle)
58+
59+
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
60+
@JvmStatic
61+
@JvmName("delete")
62+
private external fun delete(handle: Long)
63+
<%- end -%>
5564
<%- constructors.each do |constructor| -%>
5665

5766
@JvmStatic

codegen/lib/templates/kotlin/ios_class.erb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import kotlinx.cinterop.CPointer
99
actual class <%= entity.name %> constructor(
1010
val pointer: CPointer<TW<%= entity.name %>>,
1111
) {
12+
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
13+
@OptIn(ExperimentalStdlibApi::class)
14+
private val cleaner = kotlin.native.internal.createCleaner(pointer) { ptr ->
15+
TW<%= entity.name %>Delete(ptr)
16+
}
17+
<% end -%>
1218
<%# Constructors -%>
1319
<%- constructors.each do |constructor| -%>
1420

codegen/lib/templates/kotlin/js_accessors_class.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ external interface Js<%= entity.name %> {
88
<%- entity.properties.each do |property| -%>
99
fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(property.name)) %>()<%= KotlinHelper.js_return_type(property.return_type) %>
1010
<%- end -%>
11+
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
12+
fun delete()
13+
<% end -%>
1114
<% entity.methods.each do |method| -%>
1215
<% next if method.name == "Delete" -%>
1316
fun <%= KotlinHelper.fix_name(WasmCppHelper.format_name(method.name)) %>(<%= KotlinHelper.js_parameters(method.parameters.drop(1)) %>)<%= KotlinHelper.js_return_type(method.return_type) %>

codegen/lib/templates/kotlin/js_class.erb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@
66
actual class <%= entity.name %> constructor(
77
val jsValue: Js<%= entity.name %>,
88
) {
9+
<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%>
10+
private val finalizationRegistry =
11+
js(
12+
"""
13+
new FinalizationRegistry(function(heldValue) {
14+
heldValue.delete();
15+
})
16+
"""
17+
)
18+
19+
init {
20+
finalizationRegistry.register(this, jsValue)
21+
}
22+
<% end -%>
23+
924
<%# Constructors -%>
1025
<%- constructors.each do |constructor| -%>
1126

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.trustwallet.core
2+
3+
import java.lang.ref.PhantomReference
4+
import java.lang.ref.ReferenceQueue
5+
6+
internal class GenericPhantomReference private constructor(
7+
referent: Any,
8+
private val handle: Long,
9+
private val onDelete: (Long) -> Unit,
10+
) : PhantomReference<Any>(referent, queue) {
11+
12+
companion object {
13+
private val references: MutableSet<GenericPhantomReference> = HashSet()
14+
private val queue: ReferenceQueue<Any> = ReferenceQueue()
15+
16+
init {
17+
Thread {
18+
try {
19+
doDeletes()
20+
} catch (e: InterruptedException) {
21+
Thread.currentThread().interrupt()
22+
}
23+
}.apply {
24+
name = "WCFinalizingDaemon"
25+
isDaemon = true
26+
priority = Thread.NORM_PRIORITY
27+
start()
28+
}
29+
}
30+
31+
fun register(
32+
referent: Any,
33+
handle: Long,
34+
onDelete: (Long) -> Unit,
35+
) {
36+
references.add(GenericPhantomReference(referent, handle, onDelete))
37+
}
38+
39+
private fun doDeletes() {
40+
while (true) {
41+
val ref = queue.remove() as GenericPhantomReference
42+
ref.onDelete(ref.handle)
43+
references.remove(ref)
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)