Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Finalizable to ensure lexical liveness #754

Merged
merged 7 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
* Support `Credentials.function` for login user with Custom Function Authentication Provider. ([#742](https://github.com/realm/realm-dart/pull/742))
* Added `update` flag on `Realm.add` and `Realm.addAll` to support upserts. ([#668](https://github.com/realm/realm-dart/pull/668))

### Fixed
* Use Dart 2.17 `Finalizable` to ensure lexically scoped lifetime of finalizable resources (Realm, App, etc.). ([#754](https://github.com/realm/realm-dart/pull/754))

### Internal
* Added a command to `realm_dart` for deleting Atlas App Services applications. Usage: `dart run realm_dart delete-apps`. By default it will delete apps from `http://localhost:9090` which is the endpoint of the local docker image. If `--atlas-cluster` is provided, it will authenticate, delete the application from the provided cluster. (PR [#663](https://github.com/realm/realm-dart/pull/663))
* Uses Realm Core v12.5.0
Expand Down
1 change: 1 addition & 0 deletions ffigen/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ functions:
symbol-address:
include:
- 'realm_dart_.*'
- 'realm_release'
structs:
exclude:
- '_.*'
Expand Down
8 changes: 7 additions & 1 deletion lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
import 'dart:ffi';
import 'dart:io';

import 'package:meta/meta.dart';
Expand Down Expand Up @@ -108,7 +109,7 @@ class AppConfiguration {
/// * Register uses and perform various user-related operations through authentication providers
/// * Synchronize data between the local device and a remote Realm App with Synchronized Realms
/// {@category Application}
class App {
class App implements Finalizable {
final AppHandle _handle;

/// The id of this application. This is the same as the appId in the [AppConfiguration] used to
Expand Down Expand Up @@ -179,6 +180,11 @@ enum MetadataPersistenceMode {

/// @nodoc
extension AppInternal on App {
@pragma('vm:never-inline')
void keepAlive() {
_handle.keepAlive();
}

AppHandle get handle => _handle;

static App create(AppHandle handle) => App._(handle);
Expand Down
37 changes: 32 additions & 5 deletions lib/src/collections.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2022 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

import 'dart:ffi';
import 'native/realm_core.dart';

/// Contains index information about objects that moved within the same collection.
class Move {
/// The index in the old version of the collection.
final int from;

/// The index in the new version of the collection.
final int to;

Expand All @@ -22,10 +42,10 @@ class CollectionChanges {
}

/// Describes the changes in a Realm collection since the last time the notification callback was invoked.
class RealmCollectionChanges {
class RealmCollectionChanges implements Finalizable {
final RealmCollectionChangesHandle _handle;
CollectionChanges? _values;

RealmCollectionChanges(this._handle);

CollectionChanges get _changes => _values ??= realmCore.getCollectionChanges(_handle);
Expand All @@ -38,11 +58,18 @@ class RealmCollectionChanges {

/// The indexes of the objects in the new collection which were modified in this version.
List<int> get modified => _changes.modifications;

/// The indexes of the objects in the collection which moved.
List<Move> get moved => _changes.moves;

/// The indexes in the new version of the collection which were modified. Conceptually, it contains the same entries as [modified] but after the
/// The indexes in the new version of the collection which were modified. Conceptually, it contains the same entries as [modified] but after the
/// insertions and deletions have been accounted for.
List<int> get newModified => _changes.modificationsAfter;
}
}

extension RealmCollectionChangesInternal on RealmCollectionChanges {
@pragma('vm:never-inline')
void keepAlive() {
_handle.keepAlive();
}
}
9 changes: 8 additions & 1 deletion lib/src/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
//
////////////////////////////////////////////////////////////////////////////////

import 'dart:ffi';
import 'dart:io';

import 'package:path/path.dart' as _path;

import 'native/realm_core.dart';
import 'realm_class.dart';
import 'init.dart';
import 'user.dart';

/// The signature of a callback used to determine if compaction
/// should be attempted.
Expand All @@ -47,7 +49,7 @@ typedef InitialDataCallback = void Function(Realm realm);

/// Configuration used to create a [Realm] instance
/// {@category Configuration}
abstract class Configuration {
abstract class Configuration implements Finalizable {
/// The default realm filename to be used.
static String get defaultRealmName => _path.basename(defaultRealmPath);
static set defaultRealmName(String name) => defaultRealmPath = _path.join(_path.dirname(defaultRealmPath), _path.basename(name));
Expand Down Expand Up @@ -278,6 +280,11 @@ class FlexibleSyncConfiguration extends Configuration {
}

extension FlexibleSyncConfigurationInternal on FlexibleSyncConfiguration {
@pragma('vm:never-inline')
void keepAlive() {
user.keepAlive();
}

SessionStopPolicy get sessionStopPolicy => _sessionStopPolicy;
set sessionStopPolicy(SessionStopPolicy value) => _sessionStopPolicy = value;
}
Expand Down
19 changes: 15 additions & 4 deletions lib/src/credentials.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////////////////

import 'dart:convert';
import 'dart:ffi';

import 'native/realm_core.dart';
import 'app.dart';
Expand Down Expand Up @@ -55,7 +56,7 @@ enum AuthProviderType {

/// A class, representing the credentials used for authenticating a [User]
/// {@category Application}
class Credentials {
class Credentials implements Finalizable {
final RealmAppCredentialsHandle _handle;
final AuthProviderType provider;

Expand Down Expand Up @@ -98,8 +99,8 @@ class Credentials {
/// Returns a [Credentials] object that can be used to authenticate a user with a Google account using an id token.
Credentials.googleIdToken(String idToken)
: _handle = realmCore.createAppCredentialsGoogleIdToken(idToken),
provider = AuthProviderType.google;
provider = AuthProviderType.google;

/// Returns a [Credentials] object that can be used to authenticate a user with a custom Function.
/// [Custom Function Authentication Docs](https://www.mongodb.com/docs/atlas/app-services/authentication/custom-function/)
Credentials.function(String payload)
Expand All @@ -109,13 +110,18 @@ class Credentials {

/// @nodoc
extension CredentialsInternal on Credentials {
@pragma('vm:never-inline')
void keepAlive() {
_handle.keepAlive();
}

RealmAppCredentialsHandle get handle => _handle;
}

/// A class, encapsulating functionality for users, logged in with [Credentials.emailPassword()].
/// It is always scoped to a particular app.
/// {@category Application}
class EmailPasswordAuthProvider {
class EmailPasswordAuthProvider implements Finalizable {
final App app;

/// Create a new EmailPasswordAuthProvider for the [app]
Expand Down Expand Up @@ -164,5 +170,10 @@ class EmailPasswordAuthProvider {
}

extension EmailPasswordAuthProviderInternal on EmailPasswordAuthProvider {
@pragma('vm:never-inline')
void keepAlive() {
app.keepAlive();
}

static EmailPasswordAuthProvider create(App app) => EmailPasswordAuthProvider(app);
}
12 changes: 11 additions & 1 deletion lib/src/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import 'dart:async';
import 'dart:collection' as collection;
import 'dart:ffi';

import 'collections.dart';
import 'native/realm_core.dart';
Expand All @@ -29,7 +30,7 @@ import 'results.dart';
/// added to or deleted from the collection or from the Realm.
///
/// {@category Realm}
abstract class RealmList<T extends Object> with RealmEntity implements List<T> {
abstract class RealmList<T extends Object> with RealmEntity implements List<T>, Finalizable {
/// Gets a value indicating whether this collection is still valid to use.
///
/// Indicates whether the [Realm] instance hasn't been closed,
Expand Down Expand Up @@ -145,6 +146,15 @@ extension RealmListOfObject<T extends RealmObject> on RealmList<T> {

/// @nodoc
extension RealmListInternal<T extends Object> on RealmList<T> {
@pragma('vm:never-inline')
void keepAlive() {
final self = this;
if (self is ManagedRealmList<T>) {
realm.keepAlive();
self._handle.keepAlive();
}
}

ManagedRealmList<T> asManaged() => this is ManagedRealmList<T> ? this as ManagedRealmList<T> : throw RealmStateError('$this is not managed');

RealmListHandle get handle => asManaged()._handle;
Expand Down
55 changes: 2 additions & 53 deletions lib/src/native/realm_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2963,27 +2963,6 @@ class RealmLibrary {
ffi.Pointer<realm_thread_safe_reference_t> Function(
ffi.Pointer<ffi.Void>)>();

Dart_FinalizableHandle realm_dart_attach_finalizer(
Object handle,
ffi.Pointer<ffi.Void> realmPtr,
int size,
) {
return _realm_dart_attach_finalizer(
handle,
realmPtr,
size,
);
}

late final _realm_dart_attach_finalizerPtr = _lookup<
ffi.NativeFunction<
Dart_FinalizableHandle Function(ffi.Handle, ffi.Pointer<ffi.Void>,
ffi.Int)>>('realm_dart_attach_finalizer');
late final _realm_dart_attach_finalizer =
_realm_dart_attach_finalizerPtr.asFunction<
Dart_FinalizableHandle Function(
Object, ffi.Pointer<ffi.Void>, int)>();

ffi.Pointer<realm_scheduler_t> realm_dart_create_scheduler(
int isolateId,
int port,
Expand All @@ -3001,23 +2980,6 @@ class RealmLibrary {
late final _realm_dart_create_scheduler = _realm_dart_create_schedulerPtr
.asFunction<ffi.Pointer<realm_scheduler_t> Function(int, int)>();

void realm_dart_delete_finalizable(
Dart_FinalizableHandle finalizable_handle,
Object handle,
) {
return _realm_dart_delete_finalizable(
finalizable_handle,
handle,
);
}

late final _realm_dart_delete_finalizablePtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(Dart_FinalizableHandle,
ffi.Handle)>>('realm_dart_delete_finalizable');
late final _realm_dart_delete_finalizable = _realm_dart_delete_finalizablePtr
.asFunction<void Function(Dart_FinalizableHandle, Object)>();

void realm_dart_delete_persistent_handle(
ffi.Pointer<ffi.Void> handle,
) {
Expand Down Expand Up @@ -9584,22 +9546,11 @@ class RealmLibrary {
class _SymbolAddresses {
final RealmLibrary _library;
_SymbolAddresses(this._library);
ffi.Pointer<
ffi.NativeFunction<
Dart_FinalizableHandle Function(
ffi.Handle, ffi.Pointer<ffi.Void>, ffi.Int)>>
get realm_dart_attach_finalizer =>
_library._realm_dart_attach_finalizerPtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Pointer<realm_scheduler_t> Function(ffi.Uint64, Dart_Port)>>
get realm_dart_create_scheduler =>
_library._realm_dart_create_schedulerPtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(Dart_FinalizableHandle, ffi.Handle)>>
get realm_dart_delete_finalizable =>
_library._realm_dart_delete_finalizablePtr;
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
get realm_dart_delete_persistent_handle =>
_library._realm_dart_delete_persistent_handlePtr;
Expand Down Expand Up @@ -9674,10 +9625,10 @@ class _SymbolAddresses {
ffi.Pointer<ffi.NativeFunction<ffi.Handle Function(ffi.Pointer<ffi.Void>)>>
get realm_dart_weak_handle_to_object =>
_library._realm_dart_weak_handle_to_objectPtr;
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
get realm_release => _library._realm_releasePtr;
}

typedef Dart_FinalizableHandle = ffi.Pointer<_Dart_FinalizableHandle>;

/// A port is used to send or receive inter-isolate messages
typedef Dart_Port = ffi.Int64;

Expand Down Expand Up @@ -9720,8 +9671,6 @@ class UnnamedUnion2 extends ffi.Union {
external int logic_error_kind;
}

class _Dart_FinalizableHandle extends ffi.Opaque {}

class realm_app extends ffi.Opaque {}

class realm_app_config extends ffi.Opaque {}
Expand Down
Loading