Skip to content

Commit fb45a1a

Browse files
committed
Avoid zone workarounds
1 parent 2abcb03 commit fb45a1a

File tree

5 files changed

+25
-5
lines changed

5 files changed

+25
-5
lines changed

packages/powersync_core/lib/src/database/native/native_powersync_database.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class PowerSyncDatabaseImpl
120120
required PowerSyncBackendConnector connector,
121121
required Duration crudThrottleTime,
122122
required AbortController abort,
123+
required Zone asyncWorkZone,
123124
Map<String, dynamic>? params,
124125
}) async {
125126
final dbRef = database.isolateConnectionFactory();
@@ -157,7 +158,7 @@ class PowerSyncDatabaseImpl
157158
await waitForShutdown();
158159
}
159160

160-
receiveMessages.listen((data) async {
161+
Future<void> handleMessage(Object? data) async {
161162
if (data is List) {
162163
String action = data[0] as String;
163164
if (action == "getCredentials") {
@@ -192,7 +193,14 @@ class PowerSyncDatabaseImpl
192193
record.level, record.message, record.error, record.stackTrace);
193194
}
194195
}
195-
});
196+
}
197+
198+
// This function is called in a Zone marking the connection lock as locked.
199+
// This is used to prevent reentrant calls to the lock (which would be a
200+
// deadlock). However, the lock is returned as soon as this function
201+
// returns - and handleMessage may run later. So, make sure we run those
202+
// callbacks in the parent zone.
203+
receiveMessages.listen(asyncWorkZone.bindUnaryCallback(handleMessage));
196204

197205
receiveUnhandledErrors.listen((message) async {
198206
// Sample error:

packages/powersync_core/lib/src/database/powersync_database_impl_stub.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class PowerSyncDatabaseImpl
114114
{required PowerSyncBackendConnector connector,
115115
required Duration crudThrottleTime,
116116
required AbortController abort,
117+
required Zone asyncWorkZone,
117118
Map<String, dynamic>? params}) {
118119
throw UnimplementedError();
119120
}

packages/powersync_core/lib/src/database/powersync_db_mixin.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
282282

283283
clientParams = params;
284284
var thisConnectAborter = AbortController();
285+
final zone = Zone.current;
285286

286287
late void Function() retryHandler;
287288

@@ -296,6 +297,9 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
296297
crudThrottleTime: crudThrottleTime,
297298
params: params,
298299
abort: thisConnectAborter,
300+
// Run follow-up async tasks in the parent zone, a new one is introduced
301+
// while we hold the lock (and async tasks won't hold the sync lock).
302+
asyncWorkZone: zone,
299303
);
300304

301305
thisConnectAborter.onCompletion.whenComplete(retryHandler);
@@ -347,6 +351,7 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
347351
required PowerSyncBackendConnector connector,
348352
required Duration crudThrottleTime,
349353
required AbortController abort,
354+
required Zone asyncWorkZone,
350355
Map<String, dynamic>? params,
351356
});
352357

packages/powersync_core/lib/src/database/web/web_powersync_database.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class PowerSyncDatabaseImpl
116116
required PowerSyncBackendConnector connector,
117117
required Duration crudThrottleTime,
118118
required AbortController abort,
119+
required Zone asyncWorkZone,
119120
Map<String, dynamic>? params,
120121
}) async {
121122
final crudStream =

packages/powersync_core/test/streaming_sync_test.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,21 @@ void main() {
8484
final didDisconnect = Completer<void>();
8585

8686
connector.fetchCredentialsCallback = expectAsync0(() async {
87-
Zone.current.parent!.scheduleMicrotask(() {
88-
didDisconnect.complete(pdb.disconnect());
89-
});
87+
didDisconnect.complete(pdb.disconnect());
9088

9189
throw 'deliberate disconnect';
9290
});
9391

9492
service.addKeepAlive(0);
9593
await didDisconnect.future;
9694
expect(pdb.currentStatus.connected, isFalse);
95+
// The error should be cleared after calling disconnect
96+
expect(pdb.currentStatus.downloadError, isNull);
97+
98+
// Wait for a short while to make sure the database doesn't reconnect.
99+
for (var i = 0; i < 10; i++) {
100+
expect(pdb.currentStatus.connecting, isFalse);
101+
}
97102
});
98103

99104
test('can connect as initial operation', () async {

0 commit comments

Comments
 (0)