Skip to content

Commit

Permalink
RDART-1020: Fix writeAsync behaviour (#1666)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nielsenko authored May 30, 2024
1 parent 2b476a7 commit 641956d
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* None

### Fixed
* `Realm.writeAsync` did not handle async callbacks (`Future<T> 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))

Expand Down
6 changes: 5 additions & 1 deletion packages/realm_dart/lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,15 @@ class Realm {
/// Checks whether the `Realm` is in write transaction.
bool get isInTransaction => handle.isWritable;

bool _isSubtype<S, T>() => <S>[] is List<T>;
bool _isFuture<T>() => T != Never && _isSubtype<T, Future>();

/// 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>(T Function() writeCallback) {
assert(!_isFuture<T>(), 'writeCallback must be synchronous');
final transaction = beginWrite();

try {
Expand Down Expand Up @@ -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<T> writeAsync<T>(T Function() writeCallback, [CancellationToken? cancellationToken]) async {
assert(!_isFuture<T>(), 'writeCallback must be synchronous');
final transaction = await beginWriteAsync(cancellationToken);

try {
T result = writeCallback();
await transaction.commitAsync(cancellationToken);
Expand Down
12 changes: 11 additions & 1 deletion packages/realm_dart/test/realm_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ void main() {
expect(realm2.all<Person>().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'));
Expand Down Expand Up @@ -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<AssertionError>()));
});

test('Realm.write with async callback', () {
final realm = getRealm(Configuration.local([Person.schema]));
expect(() => realm.write(() async {}), throwsA(isA<AssertionError>()));
});

test('Transaction.commitAsync with a canceled token throws', () async {
final realm = getRealm(Configuration.local([Person.schema]));

Expand Down

0 comments on commit 641956d

Please sign in to comment.