Skip to content

Commit 6278fe4

Browse files
fix: run reload within a mutex (#19826)
* fix: run reload within a mutex * change total assets before emitting the reload event --------- Co-authored-by: shenlong-tanwen <[email protected]> Co-authored-by: Alex Tran <[email protected]>
1 parent dfe6d27 commit 6278fe4

File tree

1 file changed

+45
-19
lines changed

1 file changed

+45
-19
lines changed

mobile/lib/domain/services/timeline.service.dart

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ class TimelineFactory {
108108
class TimelineService {
109109
final TimelineAssetSource _assetSource;
110110
final TimelineBucketSource _bucketSource;
111+
final AsyncMutex _mutex = AsyncMutex();
112+
int _bufferOffset = 0;
113+
List<BaseAsset> _buffer = [];
114+
StreamSubscription? _bucketSubscription;
115+
111116
int _totalAssets = 0;
112117
int get totalAssets => _totalAssets;
113118

@@ -117,24 +122,41 @@ class TimelineService {
117122
}) : _assetSource = assetSource,
118123
_bucketSource = bucketSource {
119124
_bucketSubscription = _bucketSource().listen((buckets) {
120-
_totalAssets =
121-
buckets.fold<int>(0, (acc, bucket) => acc + bucket.assetCount);
122-
unawaited(_reloadBucket());
125+
_mutex.run(() async {
126+
final totalAssets =
127+
buckets.fold<int>(0, (acc, bucket) => acc + bucket.assetCount);
128+
129+
if (totalAssets == 0) {
130+
_bufferOffset = 0;
131+
_buffer.clear();
132+
} else {
133+
final int offset;
134+
final int count;
135+
// When the buffer is empty or the old bufferOffset is greater than the new total assets,
136+
// we need to reset the buffer and load the first batch of assets.
137+
if (_bufferOffset >= totalAssets || _buffer.isEmpty) {
138+
offset = 0;
139+
count = kTimelineAssetLoadBatchSize;
140+
} else {
141+
offset = _bufferOffset;
142+
count = math.min(
143+
_buffer.length,
144+
totalAssets - _bufferOffset,
145+
);
146+
}
147+
_buffer = await _assetSource(offset, count);
148+
_bufferOffset = offset;
149+
}
150+
151+
// change the state's total assets count only after the buffer is reloaded
152+
_totalAssets = totalAssets;
153+
EventStream.shared.emit(const TimelineReloadEvent());
154+
});
123155
});
124156
}
125157

126-
final AsyncMutex _mutex = AsyncMutex();
127-
int _bufferOffset = 0;
128-
List<BaseAsset> _buffer = [];
129-
StreamSubscription? _bucketSubscription;
130-
131158
Stream<List<Bucket>> Function() get watchBuckets => _bucketSource;
132159

133-
Future<void> _reloadBucket() => _mutex.run(() async {
134-
_buffer = await _assetSource(_bufferOffset, _buffer.length);
135-
EventStream.shared.emit(const TimelineReloadEvent());
136-
});
137-
138160
Future<List<BaseAsset>> loadAssets(int index, int count) =>
139161
_mutex.run(() => _loadAssets(index, count));
140162

@@ -163,18 +185,20 @@ class TimelineService {
163185
: (len > kTimelineAssetLoadBatchSize ? index : index + count - len),
164186
);
165187

166-
final assets = await _assetSource(start, len);
167-
_buffer = assets;
188+
_buffer = await _assetSource(start, len);
168189
_bufferOffset = start;
169190

170191
return getAssets(index, count);
171192
}
172193

173194
bool hasRange(int index, int count) =>
174-
index >= _bufferOffset && index + count <= _bufferOffset + _buffer.length;
195+
index >= 0 &&
196+
index < _totalAssets &&
197+
index >= _bufferOffset &&
198+
index + count <= _bufferOffset + _buffer.length &&
199+
index + count <= _totalAssets;
175200

176201
List<BaseAsset> getAssets(int index, int count) {
177-
assert(index + count <= totalAssets);
178202
if (!hasRange(index, count)) {
179203
throw RangeError('TimelineService::getAssets Index out of range');
180204
}
@@ -184,11 +208,13 @@ class TimelineService {
184208

185209
// Pre-cache assets around the given index for asset viewer
186210
Future<void> preCacheAssets(int index) =>
187-
_mutex.run(() => _loadAssets(index, math.min(5, totalAssets - index)));
211+
_mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index)));
188212

189213
BaseAsset getAsset(int index) {
190214
if (!hasRange(index, 1)) {
191-
throw RangeError('TimelineService::getAsset Index out of range');
215+
throw RangeError(
216+
'TimelineService::getAsset Index $index not in buffer range [$_bufferOffset, ${_bufferOffset + _buffer.length})',
217+
);
192218
}
193219
return _buffer.elementAt(index - _bufferOffset);
194220
}

0 commit comments

Comments
 (0)