diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23167a3..d833956 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,3 +62,6 @@ jobs: - name: Run Chrome tests run: dart test --platform chrome if: always() && steps.install.outcome == 'success' + - name: Run Chrome tests - wasm + run: dart test --platform chrome -c dart2wasm + if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' diff --git a/lib/pool.dart b/lib/pool.dart index 20ab8f1..70e9df1 100644 --- a/lib/pool.dart +++ b/lib/pool.dart @@ -239,23 +239,26 @@ class Pool { /// an error, the returned future completes with that error. /// /// This may be called more than once; it returns the same [Future] each time. - Future close() => _closeMemo.runOnce(() { - if (_closeGroup != null) return _closeGroup!.future; + Future close() => _closeMemo.runOnce(_close); - _resetTimer(); + Future _close() { + if (_closeGroup != null) return _closeGroup!.future; - _closeGroup = FutureGroup(); - for (var callback in _onReleaseCallbacks) { - _closeGroup!.add(Future.sync(callback)); - } + _resetTimer(); - _allocatedResources -= _onReleaseCallbacks.length; - _onReleaseCallbacks.clear(); + _closeGroup = FutureGroup(); + for (var callback in _onReleaseCallbacks) { + _closeGroup!.add(Future.sync(callback)); + } + + _allocatedResources -= _onReleaseCallbacks.length; + _onReleaseCallbacks.clear(); + + if (_allocatedResources == 0) _closeGroup!.close(); + return _closeGroup!.future; + } - if (_allocatedResources == 0) _closeGroup!.close(); - return _closeGroup!.future; - }); - final _closeMemo = AsyncMemoizer(); + final _closeMemo = AsyncMemoizer(); /// If there are any pending requests, this will fire the oldest one. void _onResourceReleased() { @@ -272,7 +275,7 @@ class Pool { /// If there are any pending requests, this will fire the oldest one after /// running [onRelease]. - void _onResourceReleaseAllowed(Function() onRelease) { + void _onResourceReleaseAllowed(void Function() onRelease) { _resetTimer(); if (_requestedResources.isNotEmpty) { @@ -294,7 +297,7 @@ class Pool { /// /// Futures returned by [_runOnRelease] always complete in the order they were /// created, even if earlier [onRelease] callbacks take longer to run. - Future _runOnRelease(Function() onRelease) { + Future _runOnRelease(void Function() onRelease) { Future.sync(onRelease).then((value) { _onReleaseCompleters.removeFirst().complete(PoolResource._(this)); }).catchError((Object error, StackTrace stackTrace) { @@ -367,7 +370,7 @@ class PoolResource { /// This is useful when a resource's main function is complete, but it may /// produce additional information later on. For example, an isolate's task /// may be complete, but it could still emit asynchronous errors. - void allowRelease(Function() onRelease) { + void allowRelease(FutureOr Function() onRelease) { if (_released) { throw StateError('A PoolResource may only be released once.'); } diff --git a/pubspec.yaml b/pubspec.yaml index 08e4027..7fb5021 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,6 @@ dependencies: stack_trace: ^1.10.0 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 fake_async: ^1.2.0 test: ^1.16.0 diff --git a/test/pool_test.dart b/test/pool_test.dart index 2dd90f4..6334a8a 100644 --- a/test/pool_test.dart +++ b/test/pool_test.dart @@ -41,7 +41,7 @@ void main() { // This will only complete once [lastAllocatedResource] is released. expect(pool.request(), completes); - Future.delayed(const Duration(microseconds: 1)).then((_) { + Future.delayed(const Duration(microseconds: 1)).then((_) { lastAllocatedResource.release(); }); }); @@ -55,7 +55,7 @@ void main() { test('can be called freely up to the limit', () { var pool = Pool(50); for (var i = 0; i < 50; i++) { - pool.withResource(expectAsync0(() => Completer().future)); + pool.withResource(expectAsync0(() => Completer().future)); } }); @@ -63,7 +63,7 @@ void main() { FakeAsync().run((async) { var pool = Pool(50); for (var i = 0; i < 50; i++) { - pool.withResource(expectAsync0(() => Completer().future)); + pool.withResource(expectAsync0(() => Completer().future)); } pool.withResource(expectNoAsync()); @@ -75,20 +75,20 @@ void main() { FakeAsync().run((async) { var pool = Pool(50); for (var i = 0; i < 49; i++) { - pool.withResource(expectAsync0(() => Completer().future)); + pool.withResource(expectAsync0(() => Completer().future)); } - var completer = Completer(); + var completer = Completer(); pool.withResource(() => completer.future); var blockedResourceAllocated = false; pool.withResource(() { blockedResourceAllocated = true; }); - Future.delayed(const Duration(microseconds: 1)).then((_) { + Future.delayed(const Duration(microseconds: 1)).then((_) { expect(blockedResourceAllocated, isFalse); completer.complete(); - return Future.delayed(const Duration(microseconds: 1)); + return Future.delayed(const Duration(microseconds: 1)); }).then((_) { expect(blockedResourceAllocated, isTrue); }); @@ -128,7 +128,7 @@ void main() { // This will only complete once [lastAllocatedResource] is released. expect(pool.request(), completes); - Future.delayed(const Duration(seconds: 3)).then((_) { + Future.delayed(const Duration(seconds: 3)).then((_) { lastAllocatedResource.release(); expect(pool.request(), doesNotComplete); }); @@ -146,7 +146,7 @@ void main() { } expect(pool.request(), doesNotComplete); - Future.delayed(const Duration(seconds: 3)).then((_) { + Future.delayed(const Duration(seconds: 3)).then((_) { expect(pool.request(), doesNotComplete); }); @@ -177,11 +177,11 @@ void main() { var resource = await pool.request(); var onReleaseCalled = false; resource.allowRelease(() => onReleaseCalled = true); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(onReleaseCalled, isFalse); expect(pool.request(), completes); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(onReleaseCalled, isTrue); }); @@ -195,7 +195,7 @@ void main() { var onReleaseCalled = false; resource.allowRelease(() => onReleaseCalled = true); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(onReleaseCalled, isTrue); }); @@ -206,13 +206,13 @@ void main() { var requestComplete = false; unawaited(pool.request().then((_) => requestComplete = true)); - var completer = Completer(); + var completer = Completer(); resource.allowRelease(() => completer.future); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(requestComplete, isFalse); completer.complete(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(requestComplete, isTrue); }); @@ -228,21 +228,21 @@ void main() { unawaited(pool.request().then((_) => request2Complete = true)); var onRelease1Called = false; - var completer1 = Completer(); + var completer1 = Completer(); resource1.allowRelease(() { onRelease1Called = true; return completer1.future; }); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(onRelease1Called, isTrue); var onRelease2Called = false; - var completer2 = Completer(); + var completer2 = Completer(); resource2.allowRelease(() { onRelease2Called = true; return completer2.future; }); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(onRelease2Called, isTrue); expect(request1Complete, isFalse); expect(request2Complete, isFalse); @@ -251,12 +251,12 @@ void main() { // was triggered by the second blocking request, it should complete the // first one to preserve ordering. completer2.complete(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(request1Complete, isTrue); expect(request2Complete, isFalse); completer1.complete(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); expect(request1Complete, isTrue); expect(request2Complete, isTrue); }); @@ -286,7 +286,7 @@ void main() { var resource = await pool.request(); resource.release(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); }); group('close()', () { @@ -313,7 +313,7 @@ void main() { var pool = Pool(1); var resource1 = await pool.request(); - var completer = Completer(); + var completer = Completer(); expect( pool.request().then((resource2) { expect(completer.isCompleted, isTrue); @@ -323,7 +323,7 @@ void main() { expect(pool.close(), completes); resource1.allowRelease(() => completer.future); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); completer.complete(); }); @@ -347,11 +347,11 @@ void main() { resource1Released = true; resource1.release(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); resource2Released = true; resource2.release(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); var resource3 = await resource3Future; resource3Released = true; @@ -364,7 +364,7 @@ void main() { // Set up an onRelease callback whose completion is controlled by // [completer]. - var completer = Completer(); + var completer = Completer(); resource.allowRelease(() => completer.future); expect( pool.request().then((_) { @@ -372,10 +372,10 @@ void main() { }), completes); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); unawaited(pool.close()); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); completer.complete(); }); @@ -384,9 +384,9 @@ void main() { var resource1 = await pool.request(); var resource2 = await pool.request(); - var completer1 = Completer(); + var completer1 = Completer(); resource1.allowRelease(() => completer1.future); - var completer2 = Completer(); + var completer2 = Completer(); resource2.allowRelease(() => completer2.future); expect( @@ -396,10 +396,10 @@ void main() { }), completes); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); completer1.complete(); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); completer2.complete(); }); @@ -407,17 +407,17 @@ void main() { var pool = Pool(1); var resource = await pool.request(); - var completer = Completer(); + var completer = Completer(); expect( pool.close().then((_) { expect(completer.isCompleted, isTrue); }), completes); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); resource.allowRelease(() => completer.future); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); completer.complete(); }); @@ -425,13 +425,13 @@ void main() { var pool = Pool(1); var resource = await pool.request(); - var completer = Completer(); + var completer = Completer(); resource.allowRelease(() => completer.future); expect(pool.done, throwsA('oh no!')); expect(pool.close(), throwsA('oh no!')); - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); completer.completeError('oh no!'); }); }); @@ -446,7 +446,7 @@ void main() { const delayedToStringDuration = Duration(milliseconds: 10); Future delayedToString(int i) => - Future.delayed(delayedToStringDuration, () => i.toString()); + Future.delayed(delayedToStringDuration, () => i.toString()); for (var itemCount in [0, 5]) { for (var poolSize in [1, 5, 6]) { @@ -505,7 +505,7 @@ void main() { 'before the entire iterable is iterated.'); return i; }), (i) async { - await Future.delayed(Duration(milliseconds: i)); + await Future.delayed(Duration(milliseconds: i)); return i; }); @@ -585,7 +585,7 @@ void main() { pool.close(); }); - await subscription.asFuture(); + await subscription.asFuture(); expect(dataCount, 100); await subscription.cancel(); }); @@ -616,7 +616,7 @@ void main() { if (int.parse(data) % 3 == 1) { subscription.pause(Future(() async { - await Future.delayed(const Duration(milliseconds: 100)); + await Future.delayed(const Duration(milliseconds: 100)); })); } }, @@ -690,7 +690,7 @@ void main() { pool = Pool(20); var listFuture = pool.forEach(Iterable.generate(100), (i) async { - await Future.delayed(const Duration(milliseconds: 10)); + await Future.delayed(const Duration(milliseconds: 10)); if (i == 10) { throw UnsupportedError('10 is not supported'); } @@ -705,7 +705,7 @@ void main() { var list = await pool.forEach(Iterable.generate(100), (int i) async { - await Future.delayed(const Duration(milliseconds: 10)); + await Future.delayed(const Duration(milliseconds: 10)); if (i % 10 == 0) { throw UnsupportedError('Multiples of 10 not supported'); }