Skip to content

Commit f86e6f7

Browse files
committed
Guard stale container refresh results
1 parent 916466f commit f86e6f7

1 file changed

Lines changed: 20 additions & 9 deletions

File tree

lib/data/provider/container.dart

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ abstract class ContainerState with _$ContainerState {
4040
class ContainerNotifier extends _$ContainerNotifier {
4141
var sudoCompleter = Completer<bool>();
4242
String? _cachedPassword;
43+
var _refreshGeneration = 0;
4344

4445
@override
4546
ContainerState build(
@@ -70,6 +71,7 @@ class ContainerNotifier extends _$ContainerNotifier {
7071
}
7172

7273
Future<void> setType(ContainerType type) async {
74+
final refreshGeneration = ++_refreshGeneration;
7375
state = state.copyWith(
7476
type: type,
7577
error: null,
@@ -81,7 +83,11 @@ class ContainerNotifier extends _$ContainerNotifier {
8183
);
8284
Stores.container.setType(type, hostId);
8385
sudoCompleter = Completer<bool>();
84-
await refresh();
86+
await refresh(generation: refreshGeneration);
87+
}
88+
89+
bool _isStaleRefresh(int generation) {
90+
return generation != _refreshGeneration || !ref.mounted;
8591
}
8692

8793
Future<void> _requiresSudo(
@@ -112,14 +118,17 @@ class ContainerNotifier extends _$ContainerNotifier {
112118
}
113119
}
114120

115-
Future<void> refresh({bool isAuto = false}) async {
121+
Future<void> refresh({bool isAuto = false, int? generation}) async {
116122
if (state.isBusy) return;
123+
final refreshGeneration = generation ?? _refreshGeneration;
124+
final type = state.type;
117125
state = state.copyWith(isBusy: true);
118126

119127
final sudo = sudoCompleter;
120-
if (!sudo.isCompleted) unawaited(_requiresSudo(sudo, state.type));
128+
if (!sudo.isCompleted) unawaited(_requiresSudo(sudo, type));
121129

122130
final needSudo = await sudo.future;
131+
if (_isStaleRefresh(refreshGeneration)) return;
123132

124133
/// If sudo is required and auto refresh is enabled, skip the refresh.
125134
/// Or this will ask for pwd again and again.
@@ -131,6 +140,7 @@ class ContainerNotifier extends _$ContainerNotifier {
131140
String? password;
132141
if (needSudo) {
133142
password = await _getSudoPassword();
143+
if (_isStaleRefresh(refreshGeneration)) return;
134144
if (password == null) {
135145
state = state.copyWith(
136146
isBusy: false,
@@ -147,7 +157,7 @@ class ContainerNotifier extends _$ContainerNotifier {
147157

148158
final cmd = _wrap(
149159
ContainerCmdType.execAll(
150-
state.type,
160+
type,
151161
sudo: needSudo,
152162
includeStats: includeStats,
153163
password: password,
@@ -168,14 +178,15 @@ class ContainerNotifier extends _$ContainerNotifier {
168178
},
169179
);
170180
} else {
181+
if (_isStaleRefresh(refreshGeneration)) return;
171182
state = state.copyWith(
172183
isBusy: false,
173184
error: ContainerErr(type: ContainerErrType.noClient),
174185
);
175186
return;
176187
}
177188

178-
if (!ref.mounted) return;
189+
if (_isStaleRefresh(refreshGeneration)) return;
179190
state = state.copyWith(isBusy: false);
180191

181192
if (!context.mounted) return;
@@ -254,13 +265,13 @@ class ContainerNotifier extends _$ContainerNotifier {
254265
final psRaw = ContainerCmdType.ps.find(segments);
255266
try {
256267
final lines = psRaw.split('\n');
257-
if (state.type == ContainerType.docker) {
268+
if (type == ContainerType.docker) {
258269
/// Due to the fetched data is not in json format, skip table header
259270
lines.removeWhere((element) => element.contains('CONTAINER ID'));
260271
}
261272
lines.removeWhere((element) => element.isEmpty);
262273
final items = lines
263-
.map((e) => ContainerPs.fromRaw(e, state.type))
274+
.map((e) => ContainerPs.fromRaw(e, type))
264275
.toList();
265276
state = state.copyWith(items: items);
266277
} catch (e, trace) {
@@ -279,13 +290,13 @@ class ContainerNotifier extends _$ContainerNotifier {
279290
List<ContainerImg> images;
280291
if (isEntireJson) {
281292
images = (json.decode(imageRaw) as List)
282-
.map((e) => ContainerImg.fromRawJson(json.encode(e), state.type))
293+
.map((e) => ContainerImg.fromRawJson(json.encode(e), type))
283294
.toList();
284295
} else {
285296
final lines = imageRaw.split('\n');
286297
lines.removeWhere((element) => element.isEmpty);
287298
images = lines
288-
.map((e) => ContainerImg.fromRawJson(e, state.type))
299+
.map((e) => ContainerImg.fromRawJson(e, type))
289300
.toList();
290301
}
291302
state = state.copyWith(images: images);

0 commit comments

Comments
 (0)