Skip to content

Commit

Permalink
Merge branch '21-dart-finalizer-api'
Browse files Browse the repository at this point in the history
  • Loading branch information
greenrobot-team committed Jul 11, 2023
2 parents b0afb36 + 7873e1e commit e089dda
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 145 deletions.
33 changes: 15 additions & 18 deletions objectbox/lib/src/native/admin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ import 'store.dart';
/// Note: ObjectBox Admin is currently supported for Android apps only.
/// [Additional configuration](https://docs.objectbox.io/data-browser) is
/// required.
class Admin {
class Admin implements Finalizable {
late Pointer<OBX_admin> _cAdmin;
late final Pointer<OBX_dart_finalizer> _cFinalizer;

/// Runs native close function on [_cAdmin] if this is garbage collected.
///
/// Keeps the finalizer itself reachable (static), otherwise it might be
/// disposed of before the finalizer callback gets a chance to run.
static final _finalizer = NativeFinalizer(C.addresses.admin_close.cast());

@pragma('vm:prefer-inline')
Pointer<OBX_admin> get _ptr =>
Expand Down Expand Up @@ -53,26 +58,19 @@ class Admin {

_cAdmin = C.admin(opt);

// Keep the finalizer so we can detach it when close() is called manually.
_cFinalizer = C.dartc_attach_finalizer(
this, native_admin_close, _cAdmin.cast(), 1024 * 1024);
if (_cFinalizer == nullptr) {
close();
throwLatestNativeError();
}
_finalizer.attach(this, _cAdmin.cast(),
detach: this, externalSize: 1024 * 1024);
}

/// Closes and cleans up all resources used by this Admin.
void close() {
if (!isClosed()) {
final errors = List.filled(2, 0);
if (_cFinalizer != nullptr) {
errors[0] = C.dartc_detach_finalizer(_cFinalizer, this);
}
errors[1] = C.admin_close(_cAdmin);
_cAdmin = nullptr;
errors.forEach(checkObx);
if (isClosed()) {
return;
}
_finalizer.detach(this);
final error = C.admin_close(_cAdmin);
_cAdmin = nullptr;
checkObx(error);
}

/// Returns if the admin is already closed and can no longer be used.
Expand All @@ -82,7 +80,6 @@ class Admin {
/// assigned automatically (a "0" port was used in the [bindUri]).
late final int port = () {
final result = C.admin_port(_ptr);
reachabilityFence(this);
if (result == 0) throwLatestNativeError();
return result;
}();
Expand Down
24 changes: 0 additions & 24 deletions objectbox/lib/src/native/bindings/bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -148,27 +148,3 @@ void initializeDartAPI() {
// -1 => failed to initialize - incompatible Dart version
int _dartAPIInitialized = 0;
Object? _dartAPIInitException;

/// A couple of native functions we need as callbacks to pass back to native.
/// Unfortunately, ffigen keeps those private.
typedef _NativeClose = Int Function(Pointer<Void> ptr);

final native_store_close =
_lib!.lookup<NativeFunction<_NativeClose>>('obx_store_close');
final native_query_close =
_lib!.lookup<NativeFunction<_NativeClose>>('obx_query_close');
final native_query_prop_close =
_lib!.lookup<NativeFunction<_NativeClose>>('obx_query_prop_close');
final native_admin_close =
_lib!.lookup<NativeFunction<_NativeClose>>('obx_admin_close');
final weak_store_free = _lib!
.lookup<NativeFunction<Void Function(Pointer<OBX_weak_store>)>>(
'obx_weak_store_free');

/// Keeps `this` alive until this call, preventing finalizers to run.
/// Necessary for objects with a finalizer attached because the optimizer may
/// mark the object as unused (-> GCed -> finalized) even before it's method
/// finished executing.
/// See https://github.com/dart-lang/sdk/issues/35770#issuecomment-840398463
@pragma('vm:never-inline')
Object reachabilityFence(Object obj) => obj;
15 changes: 15 additions & 0 deletions objectbox/lib/src/native/bindings/objectbox_c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8115,6 +8115,21 @@ class ObjectBoxC {
ffi.Handle)>>('obx_dart_detach_finalizer');
late final _dartc_detach_finalizer = _dartc_detach_finalizerPtr
.asFunction<int Function(ffi.Pointer<OBX_dart_finalizer>, Object)>();

late final addresses = _SymbolAddresses(this);
}

class _SymbolAddresses {
final ObjectBoxC _library;
_SymbolAddresses(this._library);
ffi.Pointer<ffi.NativeFunction<obx_err Function(ffi.Pointer<OBX_store>)>>
get store_close => _library._store_closePtr;
ffi.Pointer<ffi.NativeFunction<obx_err Function(ffi.Pointer<OBX_query>)>>
get query_close => _library._query_closePtr;
ffi.Pointer<ffi.NativeFunction<obx_err Function(ffi.Pointer<OBX_query_prop>)>>
get query_prop_close => _library._query_prop_closePtr;
ffi.Pointer<ffi.NativeFunction<obx_err Function(ffi.Pointer<OBX_admin>)>>
get admin_close => _library._admin_closePtr;
}

abstract class OBXFeature {
Expand Down
2 changes: 0 additions & 2 deletions objectbox/lib/src/native/observable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ extension ObservableStore on Store {
observer.cObserver =
C.dartc_observe_single_type(_ptr, entityId, observer.nativePort);
});
reachabilityFence(this);

return observer.stream;
}
Expand Down Expand Up @@ -126,7 +125,6 @@ extension ObservableStore on Store {
observer.init(() {
observer.cObserver = C.dartc_observe(_ptr, observer.nativePort);
}, broadcast: broadcast);
reachabilityFence(this);

if (broadcast) {
_onClose[observer] = observer.close;
Expand Down
33 changes: 11 additions & 22 deletions objectbox/lib/src/native/query/property.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
part of query;

/// Property query base.
class PropertyQuery<T> {
class PropertyQuery<T> implements Finalizable {
final Query<dynamic> _query;

/// Pointer to the native instance. Use [_ptr] for safe access instead.
final Pointer<OBX_query_prop> _cProp;
late final Pointer<OBX_dart_finalizer> _cFinalizer;

/// Runs native close function on [_cProp] if this is garbage collected.
///
/// Keeps the finalizer itself reachable (static), otherwise it might be
/// disposed of before the finalizer callback gets a chance to run.
static final _finalizer =
NativeFinalizer(C.addresses.query_prop_close.cast());

bool _closed = false;
final int _type;
bool _distinct = false;
bool _caseSensitive = false;

PropertyQuery._(this._query, this._cProp, this._type) {
checkObxPtr(_cProp, 'property query');
_cFinalizer = C.dartc_attach_finalizer(
this, native_query_prop_close, _cProp.cast(), 64);
if (_cFinalizer == nullptr) {
close();
throwLatestNativeError();
}
_finalizer.attach(this, _cProp.cast(), detach: this, externalSize: 64);
}

@pragma("vm:prefer-inline")
Expand All @@ -36,20 +38,15 @@ class PropertyQuery<T> {
void close() {
if (!_closed) {
_closed = true;
var err = 0;
if (_cFinalizer != nullptr) {
err = C.dartc_detach_finalizer(_cFinalizer, this);
}
_finalizer.detach(this);
checkObx(C.query_prop_close(_cProp));
checkObx(err);
}
}

int _count() {
final ptr = malloc<Uint64>();
try {
checkObx(C.query_prop_count(_ptr, ptr));
reachabilityFence(this);
return ptr.value;
} finally {
malloc.free(ptr);
Expand All @@ -65,7 +62,6 @@ class PropertyQuery<T> {
try {
cItems = checkObxPtr(
findFn(_ptr, cDefault ?? nullptr), 'Property query failed');
reachabilityFence(this);
return listReadFn(cItems);
} finally {
if (cDefault != null) malloc.free(cDefault);
Expand All @@ -77,7 +73,6 @@ class PropertyQuery<T> {
final ptr = malloc<Double>();
try {
checkObx(C.query_prop_avg(_ptr, ptr, nullptr));
reachabilityFence(this);
return ptr.value;
} finally {
malloc.free(ptr);
Expand All @@ -93,7 +88,6 @@ extension IntegerPropertyQuery on PropertyQuery<int> {
final ptr = malloc<Int64>();
try {
checkObx(fn(_ptr, ptr, nullptr));
reachabilityFence(this);
return ptr.value;
} finally {
malloc.free(ptr);
Expand All @@ -116,7 +110,6 @@ extension IntegerPropertyQuery on PropertyQuery<int> {
set distinct(bool d) {
_distinct = d;
checkObx(C.query_prop_distinct(_ptr, d));
reachabilityFence(this);
}

/// Minimum value of the property over all objects matching the query.
Expand Down Expand Up @@ -191,7 +184,6 @@ extension DoublePropertyQuery on PropertyQuery<double> {
final ptr = malloc<Double>();
try {
checkObx(fn(_ptr, ptr, nullptr));
reachabilityFence(this);
return ptr.value;
} finally {
malloc.free(ptr);
Expand All @@ -214,7 +206,6 @@ extension DoublePropertyQuery on PropertyQuery<double> {
set distinct(bool d) {
_distinct = d;
checkObx(C.query_prop_distinct(_ptr, d));
reachabilityFence(this);
}

/// Minimum value of the property over all objects matching the query.
Expand Down Expand Up @@ -266,7 +257,6 @@ extension StringPropertyQuery on PropertyQuery<String> {
set caseSensitive(bool caseSensitive) {
_caseSensitive = caseSensitive;
checkObx(C.query_prop_distinct_case(_ptr, _distinct, _caseSensitive));
reachabilityFence(this);
}

/// Get status of the case-sensitive configuration.
Expand All @@ -282,7 +272,6 @@ extension StringPropertyQuery on PropertyQuery<String> {
set distinct(bool d) {
_distinct = d;
checkObx(C.query_prop_distinct_case(_ptr, d, _caseSensitive));
reachabilityFence(this);
}

/// Returns the count of non-null values.
Expand Down
Loading

0 comments on commit e089dda

Please sign in to comment.