Skip to content

Commit

Permalink
Use asyncQueue instead of Timers in CachedValue. (dart-lang#7196)
Browse files Browse the repository at this point in the history
  • Loading branch information
isoos authored Nov 21, 2023
1 parent 71302aa commit d3fd9cf
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 32 deletions.
2 changes: 1 addition & 1 deletion app/lib/search/top_packages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class TopPackages {
);
}
_running = true;
await Future.wait(_values.map((v) => v.start()));
await Future.wait(_values.map((v) => v.update()));
}

@visibleForTesting
Expand Down
2 changes: 1 addition & 1 deletion app/lib/service/announcement/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class AnnouncementBackend {

/// Update announcements regularly.
Future<void> start() async {
await _announcementHtml.start();
await _announcementHtml.update();
}

/// Cancel updates and free resources.
Expand Down
2 changes: 1 addition & 1 deletion app/lib/service/youtube/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class YoutubeBackend {

/// Starts the initial and schedules the periodic updates.
Future<void> start() async {
await _packageOfWeekVideoList.start();
await _packageOfWeekVideoList.update();
}

/// Sets the list of PoW videos to a fixed value.
Expand Down
48 changes: 24 additions & 24 deletions app/lib/shared/cached_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:async';
import 'package:clock/clock.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:pub_dev/service/async_queue/async_queue.dart';

import 'monitoring.dart';

Expand Down Expand Up @@ -40,10 +41,10 @@ class CachedValue<T> {
final Duration _interval;
final Duration _timeout;
DateTime _lastUpdated = clock.now();
Timer? _timer;
T? _value;
Completer? _ongoingCompleter;
bool _closing = false;
bool _scheduled = false;

CachedValue({
required String name,
Expand All @@ -59,10 +60,30 @@ class CachedValue<T> {

DateTime get lastUpdated => _lastUpdated;
Duration get age => clock.now().difference(_lastUpdated);
bool get isAvailable => _value != null && age <= _maxAge;

bool get isAvailable {
_scheduleIfNeeded();
return _value != null && age <= _maxAge;
}

/// The cached value, may be null.
T? get value => _value;
T? get value {
_scheduleIfNeeded();
return _value;
}

void _scheduleIfNeeded() {
if (!_scheduled && !_closing && (_value == null || age > _interval)) {
_scheduled = true;
asyncQueue.addAsyncFn(() async {
try {
await _update();
} finally {
_scheduled = false;
}
});
}
}

@visibleForTesting
void setValue(T v) {
Expand All @@ -71,7 +92,6 @@ class CachedValue<T> {
}

/// Updates the cached value.
@visibleForTesting
Future<void> update() async {
if (_closing) {
throw StateError('Cache `$_name` is already closed.');
Expand Down Expand Up @@ -106,28 +126,8 @@ class CachedValue<T> {
}
}

/// Starts a periodic Timer to update the cached value.
///
/// If this is the first call of [start], and initial value
/// was not set, this will also call [update].
Future<void> start() async {
if (_closing) {
throw StateError('Cache `$_name` is already closed.');
}
if (!isAvailable && _timer == null) {
await _update();
// ignore: invariant_booleans
if (_closing) return;
}
_timer ??= Timer.periodic(_interval, (timer) {
_update();
});
}

Future<void> close() async {
_closing = true;
_timer?.cancel();
_timer = null;
if (_ongoingCompleter != null) {
await _ongoingCompleter!.future;
}
Expand Down
2 changes: 1 addition & 1 deletion app/lib/shared/popularity_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class PopularityStorage {
int lookupAsScore(String package) => (lookup(package) * 100).round();

Future<void> start() async {
await _popularity.start();
await _popularity.update();
}

Future<void> close() async {
Expand Down
13 changes: 9 additions & 4 deletions app/test/shared/cached_value_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:pub_dev/service/async_queue/async_queue.dart';
import 'package:pub_dev/shared/cached_value.dart';
import 'package:test/test.dart';

import 'test_services.dart';

void main() {
test('set value immediately available', () {
testWithProfile('set value immediately available', fn: () async {
final cv = CachedValue<String>(
name: 'test',
interval: Duration(hours: 1),
Expand All @@ -19,7 +22,7 @@ void main() {
expect(cv.value, 'x');
});

test('failing update does not crash', () async {
testWithProfile('failing update does not crash', fn: () async {
var count = 0;
final cv = CachedValue<String>(
name: 'test',
Expand All @@ -30,8 +33,10 @@ void main() {
throw Exception();
},
);
await cv.start();
await Future.delayed(Duration(seconds: 2));
for (var i = 0; i < 15; i++) {
await cv.update();
}
await asyncQueue.ongoingProcessing;
expect(cv.isAvailable, isFalse);
expect(count, greaterThan(10));
await cv.close();
Expand Down

0 comments on commit d3fd9cf

Please sign in to comment.