@@ -86,17 +86,29 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
86
86
return ;
87
87
}
88
88
89
- final nextItem = _queue.removeFirst ();
89
+ var nextItem = _queue.removeFirst ();
90
+ while (nextItem.completer.isCompleted) {
91
+ // This item already timed out - try the next one if available
92
+ if (_queue.isEmpty) {
93
+ return ;
94
+ }
95
+ nextItem = _queue.removeFirst ();
96
+ }
97
+
98
+ nextItem.lockTimer? .cancel ();
99
+
90
100
nextItem.completer.complete (Future .sync (() async {
91
101
final nextConnection = _availableReadConnections.isEmpty
92
102
? await _expandPool ()
93
103
: _availableReadConnections.removeLast ();
94
104
try {
105
+ // At this point the connection is expected to be available immediately.
106
+ // No need to calculate a new lockTimeout here.
95
107
final result = await nextConnection.readLock (nextItem.callback);
96
108
return result;
97
109
} finally {
98
110
_availableReadConnections.add (nextConnection);
99
- _nextRead ( );
111
+ Timer . run (_nextRead );
100
112
}
101
113
}));
102
114
}
@@ -110,11 +122,11 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
110
122
final zone = _getZone (debugContext: debugContext ?? 'get*()' );
111
123
final item = _PendingItem ((ctx) {
112
124
return zone.runUnary (callback, ctx);
113
- });
125
+ }, lockTimeout : lockTimeout );
114
126
_queue.add (item);
115
127
_nextRead ();
116
128
117
- return await item.completer. future;
129
+ return ( await item.future) as T ;
118
130
}
119
131
120
132
@override
@@ -207,6 +219,28 @@ typedef ReadCallback<T> = Future<T> Function(SqliteReadContext tx);
207
219
class _PendingItem {
208
220
ReadCallback <dynamic > callback;
209
221
Completer <dynamic > completer = Completer .sync ();
210
-
211
- _PendingItem (this .callback);
222
+ late Future <dynamic > future = completer.future;
223
+ DateTime ? deadline;
224
+ final Duration ? lockTimeout;
225
+ late final Timer ? lockTimer;
226
+
227
+ _PendingItem (this .callback, {this .lockTimeout}) {
228
+ if (lockTimeout != null ) {
229
+ deadline = DateTime .now ().add (lockTimeout! );
230
+ lockTimer = Timer (lockTimeout! , () {
231
+ // Note: isCompleted is true when `nextItem.completer.complete` is called, not when the result is available.
232
+ // This matches the behavior we need for a timeout on the lock, but not the entire operation.
233
+ if (! completer.isCompleted) {
234
+ // completer.completeError(
235
+ // TimeoutException('Failed to get a read connection', lockTimeout));
236
+ completer.complete (Future .sync (() async {
237
+ throw TimeoutException (
238
+ 'Failed to get a read connection' , lockTimeout);
239
+ }));
240
+ }
241
+ });
242
+ } else {
243
+ lockTimer = null ;
244
+ }
245
+ }
212
246
}
0 commit comments