From 641956d46d512c92234991c45e8288d433e0a6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Thu, 30 May 2024 17:08:34 +0200 Subject: [PATCH] RDART-1020: Fix writeAsync behaviour (#1666) * Add test that illustrates problem with async callback to writeAsync * Fix async callback issue * Update CHANGELOG * Add assert that callback to write is not async * Handle Never, which is a subtype of any type, but not a future * Disallow async callbacks even on writeAsync * Drop new async callback tests again --- CHANGELOG.md | 1 + packages/realm_dart/lib/src/realm_class.dart | 6 +++++- packages/realm_dart/test/realm_test.dart | 12 +++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8550b9c42..29e462d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * None ### Fixed +* `Realm.writeAsync` did not handle async callbacks (`Future Function()`) correctly. (Issue [#1667](https://github.com/realm/realm-dart/issues/1667)) * Fixed an issue that would cause macOS apps to be rejected with `Invalid Code Signing Entitlements` error. (Issue [#1679](https://github.com/realm/realm-dart/issues/1679)) * Fixed a regression that makes it inconvenient to run unit tests using realm. (Issue [#1619](https://github.com/realm/realm-dart/issues/1619)) diff --git a/packages/realm_dart/lib/src/realm_class.dart b/packages/realm_dart/lib/src/realm_class.dart index ad4c26862..5cc18c3fc 100644 --- a/packages/realm_dart/lib/src/realm_class.dart +++ b/packages/realm_dart/lib/src/realm_class.dart @@ -355,11 +355,15 @@ class Realm { /// Checks whether the `Realm` is in write transaction. bool get isInTransaction => handle.isWritable; + bool _isSubtype() => [] is List; + bool _isFuture() => T != Never && _isSubtype(); + /// Synchronously calls the provided callback inside a write transaction. /// /// If no exception is thrown from within the callback, the transaction will be committed. /// It is more efficient to update several properties or even create multiple objects in a single write transaction. T write(T Function() writeCallback) { + assert(!_isFuture(), 'writeCallback must be synchronous'); final transaction = beginWrite(); try { @@ -388,8 +392,8 @@ class Realm { /// Executes the provided [writeCallback] in a temporary write transaction. Both acquiring the write /// lock and committing the transaction will be done asynchronously. Future writeAsync(T Function() writeCallback, [CancellationToken? cancellationToken]) async { + assert(!_isFuture(), 'writeCallback must be synchronous'); final transaction = await beginWriteAsync(cancellationToken); - try { T result = writeCallback(); await transaction.commitAsync(cancellationToken); diff --git a/packages/realm_dart/test/realm_test.dart b/packages/realm_dart/test/realm_test.dart index d8c946499..cb7c094de 100644 --- a/packages/realm_dart/test/realm_test.dart +++ b/packages/realm_dart/test/realm_test.dart @@ -1071,7 +1071,7 @@ void main() { expect(realm2.all().length, 0); }); - test("Realm.writeAsync with multiple transactions doesnt't deadlock", () async { + test("Realm.writeAsync with multiple transactions doesn't deadlock", () async { final realm = getRealm(Configuration.local([Person.schema])); final t1 = await realm.beginWriteAsync(); realm.add(Person('Marco')); @@ -1200,6 +1200,16 @@ void main() { expect(realm.isInTransaction, false); }); + test('Realm.writeAsync with async callback fails with assert', () async { + final realm = getRealm(Configuration.local([Person.schema])); + await expectLater(realm.writeAsync(() async {}), throwsA(isA())); + }); + + test('Realm.write with async callback', () { + final realm = getRealm(Configuration.local([Person.schema])); + expect(() => realm.write(() async {}), throwsA(isA())); + }); + test('Transaction.commitAsync with a canceled token throws', () async { final realm = getRealm(Configuration.local([Person.schema]));