Skip to content

Commit b8d5944

Browse files
committed
Fix memory leaks
1 parent fd9f19b commit b8d5944

File tree

5 files changed

+115
-26
lines changed

5 files changed

+115
-26
lines changed

benchmarks/settings.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
// For CI buils, the packages are expected to have
1919
// been built and deployed to a local filesystem
2020
// maven repo.
21-
//if (System.getenv("JENKINS_HOME") == null) {
22-
// includeBuild("../packages")
23-
//}
21+
if (System.getenv("JENKINS_HOME") == null) {
22+
includeBuild("../packages")
23+
}
2424

2525
pluginManagement {
2626
repositories {

packages/cinterop/src/jvm/kotlin/io/realm/kotlin/internal/interop/RealmInterop.kt

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ actual object RealmInterop {
9191
val computedCount = properties.count { it.isComputed }
9292

9393
// Class
94-
val cclass = realm_class_info_t().apply {
94+
val cclass = realm_class_info_t_managed().apply {
9595
name = clazz.name
9696
primary_key = clazz.primaryKey
9797
num_properties = (properties.size - computedCount).toLong()
@@ -102,7 +102,7 @@ actual object RealmInterop {
102102
// Properties
103103
val classProperties = realmc.new_propertyArray(properties.size)
104104
for ((j, property) in properties.withIndex()) {
105-
val cproperty = realm_property_info_t().apply {
105+
val cproperty = realm_property_info_t_managed().apply {
106106
name = property.name
107107
public_name = property.publicName
108108
type = property.type.nativeValue
@@ -117,7 +117,18 @@ actual object RealmInterop {
117117
realmc.classArray_setitem(cclasses, i, cclass)
118118
realmc.propertyArrayArray_setitem(cproperties, i, classProperties)
119119
}
120-
return LongPointerWrapper(realmc.realm_schema_new(cclasses, count.toLong(), cproperties))
120+
try {
121+
return LongPointerWrapper(realmc.realm_schema_new(cclasses, count.toLong(), cproperties))
122+
} finally {
123+
// Clean up intermediate arrays
124+
for (classIndex in 0 until count) {
125+
val propertyArray = realmc.propertyArrayArray_getitem(cproperties, classIndex)
126+
realmc.delete_propertyArray(propertyArray)
127+
}
128+
129+
realmc.delete_propertyArrayArray(cproperties)
130+
realmc.delete_classArray(cclasses)
131+
}
121132
}
122133

123134
actual fun realm_config_new(): RealmConfigurationPointer {
@@ -316,23 +327,27 @@ actual object RealmInterop {
316327
val properties = realmc.new_propertyArray(max.toInt())
317328
val outCount = longArrayOf(0)
318329
realmc.realm_get_class_properties(realm.cptr(), classKey.key, properties, max, outCount)
319-
return if (outCount[0] > 0) {
320-
(0 until outCount[0]).map { i ->
321-
with(realmc.propertyArray_getitem(properties, i.toInt())) {
322-
PropertyInfo(
323-
name,
324-
public_name,
325-
PropertyType.from(type),
326-
CollectionType.from(collection_type),
327-
link_target,
328-
link_origin_property_name,
329-
PropertyKey(key),
330-
flags
331-
)
330+
try {
331+
return if (outCount[0] > 0) {
332+
(0 until outCount[0]).map { i ->
333+
with(realmc.propertyArray_getitem(properties, i.toInt())) {
334+
PropertyInfo(
335+
name,
336+
public_name,
337+
PropertyType.from(type),
338+
CollectionType.from(collection_type),
339+
link_target,
340+
link_origin_property_name,
341+
PropertyKey(key),
342+
flags
343+
)
344+
}
332345
}
346+
} else {
347+
emptyList()
333348
}
334-
} else {
335-
emptyList()
349+
} finally {
350+
realmc.delete_propertyArray(properties)
336351
}
337352
}
338353

@@ -922,6 +937,8 @@ actual object RealmInterop {
922937
builder.initIndicesArray(builder::modificationIndices, modificationIndices)
923938
builder.initIndicesArray(builder::modificationIndicesAfter, modificationIndicesAfter)
924939
builder.movesCount = movesCount[0].toInt()
940+
941+
realmc.delete_collectionMoveArray(moves)
925942
}
926943

927944
actual fun <T, R> realm_collection_changes_get_ranges(
@@ -969,6 +986,12 @@ actual object RealmInterop {
969986
builder.initRangesArray(builder::insertionRanges, insertionRanges, insertRangesCount[0])
970987
builder.initRangesArray(builder::modificationRanges, modificationRanges, modificationRangesCount[0])
971988
builder.initRangesArray(builder::modificationRangesAfter, modificationRangesAfter, modificationRangesCount[0])
989+
990+
realmc.delete_indexRangeArray(insertionRanges)
991+
realmc.delete_indexRangeArray(modificationRanges)
992+
realmc.delete_indexRangeArray(modificationRangesAfter)
993+
realmc.delete_indexRangeArray(deletionRanges)
994+
realmc.delete_collectionMoveArray(moves)
972995
}
973996

974997
actual fun <R> realm_dictionary_get_changes(
@@ -1107,6 +1130,8 @@ actual object RealmInterop {
11071130
}
11081131
} else {
11091132
emptyList()
1133+
}.also {
1134+
realmc.delete_identityArray(keys)
11101135
}
11111136
}
11121137

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2023 Realm Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@file:Suppress("ClassNaming")
17+
18+
package io.realm.kotlin.internal.interop
19+
20+
/**
21+
* realm_class_info_t variant that automatically frees any heap allocated resources
22+
*/
23+
class realm_class_info_t_managed : realm_class_info_t() {
24+
@Synchronized
25+
override fun delete() {
26+
if (realm_class_info_t.getCPtr(this) != 0L) {
27+
realmc.realm_class_info_t_cleanup(this)
28+
}
29+
super.delete()
30+
}
31+
}
32+
33+
/**
34+
* realm_property_info_t variant that automatically frees any heap allocated resources
35+
*/
36+
class realm_property_info_t_managed : realm_property_info_t() {
37+
@Synchronized
38+
override fun delete() {
39+
if (realm_property_info_t.getCPtr(this) != 0L) {
40+
realmc.realm_property_info_t_cleanup(this)
41+
}
42+
super.delete()
43+
}
44+
}

packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -721,10 +721,10 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error)
721721
jboolean is_unrecognized_by_client = error.is_unrecognized_by_client;
722722
jboolean is_client_reset_requested = error.is_client_reset_requested;
723723

724-
auto user_info_map = new std::map<std::string, std::string>();
724+
auto user_info_map = std::map<std::string, std::string>();
725725
for (int i = 0; i < error.user_info_length; i++) {
726726
realm_sync_error_user_info_t user_info = error.user_info_map[i];
727-
user_info_map->insert(std::make_pair(user_info.key, user_info.value));
727+
user_info_map.insert(std::make_pair(user_info.key, user_info.value));
728728
}
729729

730730
static JavaMethod core_compensating_write_info_constructor(
@@ -766,16 +766,16 @@ jobject convert_to_jvm_sync_error(JNIEnv* jenv, const realm_sync_error_t& error)
766766
// mark the file for deletion. Having 'original path' in the user_info_map is a side effect of
767767
// using the same code for client reset.
768768
if (error.user_info_length > 0) {
769-
auto end_it = user_info_map->end();
769+
auto end_it = user_info_map.end();
770770

771-
auto original_it = user_info_map->find(error.c_original_file_path_key);
771+
auto original_it = user_info_map.find(error.c_original_file_path_key);
772772
if (end_it != original_it) {
773773
auto original_file_path = original_it->second;
774774
joriginal_file_path = to_jstring(jenv, original_file_path);
775775
}
776776

777777
// Sync errors may not have the path to the recovery file unless a Client Reset is requested
778-
auto recovery_it = user_info_map->find(error.c_recovery_file_path_key);
778+
auto recovery_it = user_info_map.find(error.c_recovery_file_path_key);
779779
if (error.is_client_reset_requested && (end_it != recovery_it)) {
780780
auto recovery_file_path = recovery_it->second;
781781
jrecovery_file_path = to_jstring(jenv, recovery_file_path);
@@ -1053,3 +1053,17 @@ realm_scheduler_t*
10531053
realm_create_generic_scheduler() {
10541054
return new realm_scheduler_t { realm::util::Scheduler::make_dummy() };
10551055
}
1056+
1057+
void
1058+
realm_property_info_t_cleanup(realm_property_info_t* value) {
1059+
delete[] value->link_origin_property_name;
1060+
delete[] value->link_target;
1061+
delete[] value->name;
1062+
delete[] value->public_name;
1063+
}
1064+
1065+
void
1066+
realm_class_info_t_cleanup(realm_class_info_t * value) {
1067+
delete[] value->primary_key;
1068+
delete[] value->name;
1069+
}

packages/jni-swig-stub/src/main/jni/realm_api_helpers.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,10 @@ realm_sync_thread_error(realm_userdata_t userdata, const char* error);
137137
realm_scheduler_t*
138138
realm_create_generic_scheduler();
139139

140+
void
141+
realm_property_info_t_cleanup(realm_property_info_t* value);
142+
143+
void
144+
realm_class_info_t_cleanup(realm_class_info_t * value);
145+
140146
#endif //TEST_REALM_API_HELPERS_H

0 commit comments

Comments
 (0)