Commit a7c3bf48 authored by Devon Carew's avatar Devon Carew Committed by GitHub

perform the initial poll for devices quicker (#11356)

* perform the initial poll for devices quicker

* add a Poller class

* test the new Poller class
parent bbcfb8d5
...@@ -10,6 +10,7 @@ import 'package:crypto/crypto.dart'; ...@@ -10,6 +10,7 @@ import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:quiver/time.dart'; import 'package:quiver/time.dart';
import '../globals.dart';
import 'context.dart'; import 'context.dart';
import 'file_system.dart'; import 'file_system.dart';
import 'platform.dart'; import 'platform.dart';
...@@ -216,3 +217,42 @@ class Uuid { ...@@ -216,3 +217,42 @@ class Uuid {
} }
Clock get clock => context.putIfAbsent(Clock, () => const Clock()); Clock get clock => context.putIfAbsent(Clock, () => const Clock());
typedef Future<Null> AsyncCallback();
/// A [Timer] inspired class that:
/// - has a different initial value for the first callback delay
/// - waits for a callback to be complete before it starts the next timer
class Poller {
Poller(this.callback, this.pollingInterval, { this.initialDelay: Duration.ZERO }) {
new Future<Null>.delayed(initialDelay, _handleCallback);
}
final AsyncCallback callback;
final Duration initialDelay;
final Duration pollingInterval;
bool _cancelled = false;
Timer _timer;
Future<Null> _handleCallback() async {
if (_cancelled)
return;
try {
await callback();
} catch (error) {
printTrace('Error from poller: $error');
}
if (!_cancelled)
_timer = new Timer(pollingInterval, _handleCallback);
}
/// Cancels the poller.
void cancel() {
_cancelled = true;
_timer?.cancel();
_timer = null;
}
}
...@@ -116,35 +116,28 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -116,35 +116,28 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
final String name; final String name;
ItemListNotifier<Device> _items; ItemListNotifier<Device> _items;
Timer _timer; Poller _poller;
Future<List<Device>> pollingGetDevices(); Future<List<Device>> pollingGetDevices();
void startPolling() { void startPolling() {
if (_timer == null) { if (_poller == null) {
_items ??= new ItemListNotifier<Device>(); _items ??= new ItemListNotifier<Device>();
bool _fetchingDevices = false;
_timer = new Timer.periodic(_pollingInterval, (Timer timer) async { _poller = new Poller(() async {
if (_fetchingDevices) {
printTrace('Skipping device poll: already in progress');
return;
}
_fetchingDevices = true;
try { try {
final List<Device> devices = await pollingGetDevices().timeout(_pollingTimeout); final List<Device> devices = await pollingGetDevices().timeout(_pollingTimeout);
_items.updateWithNewList(devices); _items.updateWithNewList(devices);
} on TimeoutException { } on TimeoutException {
printTrace('Device poll timed out.'); printTrace('Device poll timed out.');
} finally {
_fetchingDevices = false;
} }
}); }, _pollingInterval);
} }
} }
void stopPolling() { void stopPolling() {
_timer?.cancel(); _poller?.cancel();
_timer = null; _poller = null;
} }
@override @override
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'package:flutter_tools/src/base/utils.dart'; import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/base/version.dart'; import 'package:flutter_tools/src/base/version.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -115,4 +117,52 @@ baz=qux ...@@ -115,4 +117,52 @@ baz=qux
expect(new Version.parse('Preview2.2'), isNull); expect(new Version.parse('Preview2.2'), isNull);
}); });
}); });
group('Poller', () {
const Duration kShortDelay = const Duration(milliseconds: 100);
Poller poller;
tearDown(() {
poller?.cancel();
});
test('fires at start', () async {
bool called = false;
poller = new Poller(() {
called = true;
}, const Duration(seconds: 1));
expect(called, false);
await new Future<Null>.delayed(kShortDelay);
expect(called, true);
});
test('runs periodically', () async {
// Ensure we get the first (no-delay) callback, and one of the periodic callbacks.
int callCount = 0;
poller = new Poller(() {
callCount++;
}, new Duration(milliseconds: kShortDelay.inMilliseconds ~/ 2));
expect(callCount, 0);
await new Future<Null>.delayed(kShortDelay);
expect(callCount, greaterThanOrEqualTo(2));
});
test('no quicker then the periodic delay', () async {
// Make sure that the poller polls at delay + the time it took to run the callback.
final Completer<Duration> completer = new Completer<Duration>();
DateTime firstTime;
poller = new Poller(() async {
if (firstTime == null)
firstTime = new DateTime.now();
else
completer.complete(new DateTime.now().difference(firstTime));
// introduce a delay
await new Future<Null>.delayed(kShortDelay);
}, kShortDelay);
final Duration duration = await completer.future;
expect(duration, greaterThanOrEqualTo(new Duration(milliseconds: kShortDelay.inMilliseconds * 2)));
});
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment