Skip to content

Commit 835c59a

Browse files
authored
query and property query - add a reachabilityFence() after each use of a native pointer with a finalizer attached to prevent premature finalization. (#243)
1 parent 6629058 commit 835c59a

File tree

3 files changed

+50
-10
lines changed

3 files changed

+50
-10
lines changed

objectbox/lib/src/native/bindings/bindings.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,13 @@ late final native_query_close =
122122
_lib!.lookup<NativeFunction<_native_close>>('obx_query_close');
123123
late final native_query_prop_close =
124124
_lib!.lookup<NativeFunction<_native_close>>('obx_query_prop_close');
125+
126+
/// Keeps `this` alive until this call, preventing finalizers to run.
127+
/// Necessary for objects with a finalizer attached because the optimizer may
128+
/// mark the object as unused (-> GCed -> finalized) even before it's method
129+
/// finished executing.
130+
/// See https://github.com/dart-lang/sdk/issues/35770#issuecomment-840398463
131+
@pragma('vm:never-inline')
132+
Object reachabilityFence(Object obj) {
133+
return obj;
134+
}

objectbox/lib/src/native/query/property.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ class PropertyQuery<T> {
99
bool _distinct = false;
1010
bool _caseSensitive = false;
1111

12-
PropertyQuery._(Pointer<OBX_query> cQuery, ModelProperty property)
13-
: _type = property.type,
14-
_cProp = checkObxPtr(
15-
C.query_prop(cQuery, property.id.id), 'property query') {
12+
PropertyQuery._(this._cProp, this._type) {
13+
checkObxPtr(_cProp, 'property query');
1614
_cFinalizer = C.dartc_attach_finalizer(
1715
this, native_query_prop_close, _cProp.cast(), 64);
1816
if (_cFinalizer == nullptr) {
@@ -38,6 +36,7 @@ class PropertyQuery<T> {
3836
final ptr = malloc<Uint64>();
3937
try {
4038
checkObx(C.query_prop_count(_cProp, ptr));
39+
reachabilityFence(this);
4140
return ptr.value;
4241
} finally {
4342
malloc.free(ptr);
@@ -53,6 +52,7 @@ class PropertyQuery<T> {
5352
try {
5453
cItems = checkObxPtr(
5554
findFn(_cProp, cDefault ?? nullptr), 'Property query failed');
55+
reachabilityFence(this);
5656
return listReadFn(cItems);
5757
} finally {
5858
if (cDefault != null) malloc.free(cDefault);
@@ -64,6 +64,7 @@ class PropertyQuery<T> {
6464
final ptr = malloc<Double>();
6565
try {
6666
checkObx(C.query_prop_avg(_cProp, ptr, nullptr));
67+
reachabilityFence(this);
6768
return ptr.value;
6869
} finally {
6970
malloc.free(ptr);
@@ -79,6 +80,7 @@ extension IntegerPropertyQuery on PropertyQuery<int> {
7980
final ptr = malloc<Int64>();
8081
try {
8182
checkObx(fn(_cProp, ptr, nullptr));
83+
reachabilityFence(this);
8284
return ptr.value;
8385
} finally {
8486
malloc.free(ptr);
@@ -101,6 +103,7 @@ extension IntegerPropertyQuery on PropertyQuery<int> {
101103
set distinct(bool d) {
102104
_distinct = d;
103105
checkObx(C.query_prop_distinct(_cProp, d));
106+
reachabilityFence(this);
104107
}
105108

106109
/// Minimum value of the property over all objects matching the query.
@@ -175,6 +178,7 @@ extension DoublePropertyQuery on PropertyQuery<double> {
175178
final ptr = malloc<Double>();
176179
try {
177180
checkObx(fn(_cProp, ptr, nullptr));
181+
reachabilityFence(this);
178182
return ptr.value;
179183
} finally {
180184
malloc.free(ptr);
@@ -197,6 +201,7 @@ extension DoublePropertyQuery on PropertyQuery<double> {
197201
set distinct(bool d) {
198202
_distinct = d;
199203
checkObx(C.query_prop_distinct(_cProp, d));
204+
reachabilityFence(this);
200205
}
201206

202207
/// Minimum value of the property over all objects matching the query.
@@ -248,6 +253,7 @@ extension StringPropertyQuery on PropertyQuery<String> {
248253
set caseSensitive(bool caseSensitive) {
249254
_caseSensitive = caseSensitive;
250255
checkObx(C.query_prop_distinct_case(_cProp, _distinct, _caseSensitive));
256+
reachabilityFence(this);
251257
}
252258

253259
/// Get status of the case-sensitive configuration.
@@ -263,6 +269,7 @@ extension StringPropertyQuery on PropertyQuery<String> {
263269
set distinct(bool d) {
264270
_distinct = d;
265271
checkObx(C.query_prop_distinct_case(_cProp, d, _caseSensitive));
272+
reachabilityFence(this);
266273
}
267274

268275
/// Returns the count of non-null values.

objectbox/lib/src/native/query/query.dart

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ class _ByteVectorCondition<EntityT>
538538
func) =>
539539
withNativeBytes(
540540
_value,
541-
(Pointer<Uint8> ptr, int size) =>
541+
(Pointer<Uint8> ptr, int size) =>
542542
func(builder._cBuilder, _property._model.id.id, ptr, size));
543543

544544
@override
@@ -652,7 +652,11 @@ class Query<T> {
652652
/// the whole result, e.g. for "result paging".
653653
///
654654
/// Set offset=0 to reset to the default - starting from the first element.
655-
set offset(int offset) => checkObx(C.query_offset(_ptr, offset));
655+
set offset(int offset) {
656+
final result = checkObx(C.query_offset(_ptr, offset));
657+
reachabilityFence(this);
658+
return result;
659+
}
656660

657661
/// Configure a [limit] for this query.
658662
///
@@ -661,13 +665,18 @@ class Query<T> {
661665
/// the whole result, e.g. for "result paging".
662666
///
663667
/// Set limit=0 to reset to the default behavior - no limit applied.
664-
set limit(int limit) => checkObx(C.query_limit(_ptr, limit));
668+
set limit(int limit) {
669+
final result = checkObx(C.query_limit(_ptr, limit));
670+
reachabilityFence(this);
671+
return result;
672+
}
665673

666674
/// Returns the number of matching Objects.
667675
int count() {
668676
final ptr = malloc<Uint64>();
669677
try {
670678
checkObx(C.query_count(_ptr, ptr));
679+
reachabilityFence(this);
671680
return ptr.value;
672681
} finally {
673682
malloc.free(ptr);
@@ -710,12 +719,14 @@ class Query<T> {
710719
_store.runInTransaction(TxMode.read, () {
711720
checkObx(C.query_visit(_ptr, visitor, nullptr));
712721
});
722+
reachabilityFence(this);
713723
return result;
714724
}
715725

716726
/// Finds Objects matching the query and returns their IDs.
717727
List<int> findIds() {
718728
final idArrayPtr = checkObxPtr(C.query_find_ids(_ptr), 'find ids');
729+
reachabilityFence(this);
719730
try {
720731
final idArray = idArrayPtr.ref;
721732
return idArray.count == 0
@@ -732,6 +743,7 @@ class Query<T> {
732743
final collector = objectCollector(result, _store, _entity);
733744
_store.runInTransaction(
734745
TxMode.read, () => checkObx(C.query_visit(_ptr, collector, nullptr)));
746+
reachabilityFence(this);
735747
return result;
736748
}
737749

@@ -754,6 +766,7 @@ class Query<T> {
754766
closed = true;
755767
C.dartc_stream_close(cStream);
756768
port.close();
769+
reachabilityFence(this);
757770
};
758771

759772
try {
@@ -838,10 +851,18 @@ class Query<T> {
838851
// }
839852

840853
/// For internal testing purposes.
841-
String describe() => dartStringFromC(C.query_describe(_ptr));
854+
String describe() {
855+
final result = dartStringFromC(C.query_describe(_ptr));
856+
reachabilityFence(this);
857+
return result;
858+
}
842859

843860
/// For internal testing purposes.
844-
String describeParameters() => dartStringFromC(C.query_describe_params(_ptr));
861+
String describeParameters() {
862+
final result = dartStringFromC(C.query_describe_params(_ptr));
863+
reachabilityFence(this);
864+
return result;
865+
}
845866

846867
/// Use the same query conditions but only return a single property (field).
847868
///
@@ -852,7 +873,9 @@ class Query<T> {
852873
/// var results = query.property(tInteger).find();
853874
/// ```
854875
PropertyQuery<DartType> property<DartType>(QueryProperty<T, DartType> prop) {
855-
final result = PropertyQuery<DartType>._(_ptr, prop._model);
876+
final result = PropertyQuery<DartType>._(
877+
C.query_prop(_ptr, prop._model.id.id), prop._model.type);
878+
reachabilityFence(this);
856879
if (prop._model.type == OBXPropertyType.String) {
857880
result._caseSensitive = InternalStoreAccess.queryCS(_store);
858881
}

0 commit comments

Comments
 (0)