Skip to content

Commit ae7dc0e

Browse files
authored
Fix web database not respecting lock timeouts (#88)
1 parent bb65cfa commit ae7dc0e

File tree

3 files changed

+25
-5
lines changed

3 files changed

+25
-5
lines changed

packages/sqlite_async/lib/src/web/database.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class WebDatabase
9494
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
9595
{Duration? lockTimeout, String? debugContext}) async {
9696
if (_mutex case var mutex?) {
97-
return await mutex.lock(() async {
97+
return await mutex.lock(timeout: lockTimeout, () async {
9898
final context = _SharedContext(this);
9999
try {
100100
return await callback(context);
@@ -143,7 +143,7 @@ class WebDatabase
143143
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
144144
{Duration? lockTimeout, String? debugContext, bool? flush}) async {
145145
if (_mutex case var mutex?) {
146-
return await mutex.lock(() async {
146+
return await mutex.lock(timeout: lockTimeout, () async {
147147
final context = _ExclusiveContext(this);
148148
try {
149149
return await callback(context);

packages/sqlite_async/lib/src/web/web_mutex.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ external Navigator get _navigator;
1616
/// Web implementation of [Mutex]
1717
class MutexImpl implements Mutex {
1818
late final mutex.Mutex fallback;
19-
String? identifier;
2019
final String resolvedIdentifier;
2120

22-
MutexImpl({this.identifier})
21+
MutexImpl({String? identifier})
2322

2423
/// On web a lock name is required for Navigator locks.
2524
/// Having exclusive Mutex instances requires a somewhat unique lock name.
@@ -40,7 +39,7 @@ class MutexImpl implements Mutex {
4039

4140
@override
4241
Future<T> lock<T>(Future<T> Function() callback, {Duration? timeout}) {
43-
if ((_navigator as JSObject).hasProperty('locks'.toJS).toDart) {
42+
if (_navigator.has('locks')) {
4443
return _webLock(callback, timeout: timeout);
4544
} else {
4645
return _fallbackLock(callback, timeout: timeout);

packages/sqlite_async/test/basic_test.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,27 @@ void main() {
209209
expect(await db.get('SELECT description FROM test_data'),
210210
{'description': 'test'});
211211
});
212+
213+
test('respects lock timeouts', () async {
214+
// Unfortunately this test can't use fakeAsync because it uses actual
215+
// lock APIs on the web.
216+
final db = await testUtils.setupDatabase(path: path);
217+
final lockAcquired = Completer();
218+
219+
final completion = db.writeLock((context) async {
220+
lockAcquired.complete();
221+
await Future.delayed(const Duration(seconds: 1));
222+
});
223+
224+
await lockAcquired.future;
225+
await expectLater(
226+
() => db.writeLock(
227+
lockTimeout: Duration(milliseconds: 200), (_) async => {}),
228+
throwsA(isA<TimeoutException>()),
229+
);
230+
231+
await completion;
232+
});
212233
});
213234
}
214235

0 commit comments

Comments
 (0)