Merge the null_safety branch into master (#40)
diff --git a/.travis.yml b/.travis.yml index 18f7413..9061589 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -1,18 +1,39 @@ language: dart dart: - - 2.0.0 - - dev + - dev -dart_task: - - test: --platform vm,chrome - - dartfmt - - dartanalyzer: --fatal-infos --fatal-warnings . +jobs: + include: + - stage: analyze_and_format + name: "Analyze" + dart: dev + os: linux + script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . + - stage: analyze_and_format + name: "Format" + dart: dev + os: linux + script: dartfmt -n --set-exit-if-changed . + - stage: test + name: "Vm Tests" + dart: dev + os: linux + script: pub run --enable-experiment=non-nullable test -p vm + - stage: test + name: "Web Tests" + dart: dev + os: linux + script: pub run --enable-experiment=non-nullable test -p chrome + +stages: + - analyze_and_format + - test # Only building master means that we don't run two builds for each pull request. branches: - only: [master] + only: [master, null_safety] cache: - directories: - - $HOME/.pub-cache + directories: + - $HOME/.pub-cache
diff --git a/CHANGELOG.md b/CHANGELOG.md index c680dd2..a413d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,5 +1,6 @@ -## 1.4.1 +## 1.5.0-nullsafety +* Migrate to null safety. * `forEach`: Avoid `await null` if the `Stream` is not paused. Improves trivial benchmark by 40%.
diff --git a/analysis_options.yaml b/analysis_options.yaml index 8bef881..986bccc 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml
@@ -4,6 +4,9 @@ strong-mode: implicit-casts: false + enable-experiment: + - non-nullable + linter: rules: - avoid_function_literals_in_foreach_calls @@ -36,6 +39,8 @@ - prefer_initializing_formals - prefer_interpolation_to_compose_strings - prefer_typing_uninitialized_variables + - recursive_getters + - slash_for_doc_comments - test_types_in_equals - throw_in_finally - unnecessary_await_in_return
diff --git a/benchmark/for_each_benchmark.dart b/benchmark/for_each_benchmark.dart index 7438ace..2973b2a 100644 --- a/benchmark/for_each_benchmark.dart +++ b/benchmark/for_each_benchmark.dart
@@ -8,22 +8,22 @@ final watch = Stopwatch()..start(); final start = DateTime.now(); - DateTime lastLog; - Duration fastest; - int fastestIteration; + DateTime? lastLog; + Duration? fastest; + late int fastestIteration; var i = 1; void log(bool force) { var now = DateTime.now(); if (force || lastLog == null || - now.difference(lastLog) > const Duration(seconds: 1)) { + now.difference(lastLog!) > const Duration(seconds: 1)) { lastLog = now; print([ now.difference(start), i.toString().padLeft(10), fastestIteration.toString().padLeft(7), - fastest.inMicroseconds.toString().padLeft(9) + fastest!.inMicroseconds.toString().padLeft(9) ].join(' ')); } }
diff --git a/lib/pool.dart b/lib/pool.dart index d856541..20ab8f1 100644 --- a/lib/pool.dart +++ b/lib/pool.dart
@@ -51,16 +51,16 @@ /// for additional files to be read before they could be closed. /// /// This is `null` if this pool shouldn't time out. - RestartableTimer _timer; + RestartableTimer? _timer; /// The amount of time to wait before timing out the pending resources. - final Duration _timeout; + final Duration? _timeout; /// A [FutureGroup] that tracks all the `onRelease` callbacks for resources /// that have been marked releasable. /// /// This is `null` until [close] is called. - FutureGroup _closeGroup; + FutureGroup? _closeGroup; /// Whether [close] has been called. bool get isClosed => _closeMemo.hasRun; @@ -78,7 +78,7 @@ /// If [timeout] is passed, then if that much time passes without any activity /// all pending [request] futures will throw a [TimeoutException]. This is /// intended to avoid deadlocks. - Pool(this._maxAllocatedResources, {Duration timeout}) : _timeout = timeout { + Pool(this._maxAllocatedResources, {Duration? timeout}) : _timeout = timeout { if (_maxAllocatedResources <= 0) { throw ArgumentError.value(_maxAllocatedResources, 'maxAllocatedResources', 'Must be greater than zero.'); @@ -152,17 +152,17 @@ /// to, a [StateError] is thrown. Stream<T> forEach<S, T>( Iterable<S> elements, FutureOr<T> Function(S source) action, - {bool Function(S item, Object error, StackTrace stack) onError}) { + {bool Function(S item, Object error, StackTrace stack)? onError}) { onError ??= (item, e, s) => true; var cancelPending = false; - Completer resumeCompleter; - StreamController<T> controller; + Completer? resumeCompleter; + late StreamController<T> controller; - Iterator<S> iterator; + late Iterator<S> iterator; - Future<void> run(int i) async { + Future<void> run(int _) async { while (iterator.moveNext()) { // caching `current` is necessary because there are async breaks // in this code and `iterator` is shared across many workers @@ -171,7 +171,7 @@ _resetTimer(); if (resumeCompleter != null) { - await resumeCompleter.future; + await resumeCompleter!.future; } if (cancelPending) { @@ -182,7 +182,7 @@ try { value = await action(current); } catch (e, stack) { - if (onError(current, e, stack)) { + if (onError!(current, e, stack)) { controller.addError(e, stack); } continue; @@ -191,20 +191,19 @@ } } - Future doneFuture; + Future<void>? doneFuture; void onListen() { - assert(iterator == null); iterator = elements.iterator; assert(doneFuture == null); - doneFuture = Future.wait( - Iterable<int>.generate(_maxAllocatedResources) - .map((i) => withResource(() => run(i))), - eagerError: true) + var futures = Iterable<Future<void>>.generate( + _maxAllocatedResources, (i) => withResource(() => run(i))); + doneFuture = Future.wait(futures, eagerError: true) + .then<void>((_) {}) .catchError(controller.addError); - doneFuture.whenComplete(controller.close); + doneFuture!.whenComplete(controller.close); } controller = StreamController<T>( @@ -217,11 +216,11 @@ }, onPause: () { assert(resumeCompleter == null); - resumeCompleter = Completer(); + resumeCompleter = Completer<void>(); }, onResume: () { assert(resumeCompleter != null); - resumeCompleter.complete(); + resumeCompleter!.complete(); resumeCompleter = null; }, ); @@ -241,20 +240,20 @@ /// /// This may be called more than once; it returns the same [Future] each time. Future close() => _closeMemo.runOnce(() { - if (_closeGroup != null) return _closeGroup.future; + if (_closeGroup != null) return _closeGroup!.future; _resetTimer(); _closeGroup = FutureGroup(); for (var callback in _onReleaseCallbacks) { - _closeGroup.add(Future.sync(callback)); + _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(); @@ -267,7 +266,7 @@ pending.complete(PoolResource._(this)); } else { _allocatedResources--; - if (isClosed && _allocatedResources == 0) _closeGroup.close(); + if (isClosed && _allocatedResources == 0) _closeGroup!.close(); } } @@ -280,9 +279,9 @@ var pending = _requestedResources.removeFirst(); pending.complete(_runOnRelease(onRelease)); } else if (isClosed) { - _closeGroup.add(Future.sync(onRelease)); + _closeGroup!.add(Future.sync(onRelease)); _allocatedResources--; - if (_allocatedResources == 0) _closeGroup.close(); + if (_allocatedResources == 0) _closeGroup!.close(); } else { var zone = Zone.current; var registered = zone.registerCallback(onRelease); @@ -298,7 +297,7 @@ Future<PoolResource> _runOnRelease(Function() onRelease) { Future.sync(onRelease).then((value) { _onReleaseCompleters.removeFirst().complete(PoolResource._(this)); - }).catchError((error, StackTrace stackTrace) { + }).catchError((Object error, StackTrace stackTrace) { _onReleaseCompleters.removeFirst().completeError(error, stackTrace); }); @@ -312,9 +311,9 @@ if (_timer == null) return; if (_requestedResources.isEmpty) { - _timer.cancel(); + _timer!.cancel(); } else { - _timer.reset(); + _timer!.reset(); } }
diff --git a/pubspec.yaml b/pubspec.yaml index c87617f..8fc0e14 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -1,5 +1,5 @@ name: pool -version: 1.4.1-dev +version: 1.5.0-nullsafety description: >- Manage a finite pool of resources. @@ -7,13 +7,96 @@ homepage: https://github.com/dart-lang/pool environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.9.0-18.0 <2.9.0' dependencies: - async: '>=1.4.0 <3.0.0' - stack_trace: '>=0.9.2 <2.0.0' + async: '>=2.5.0-nullsafety <2.5.0' + stack_trace: '>=1.10.0-nullsafety <1.10.0' dev_dependencies: fake_async: ^1.0.0 pedantic: ^1.4.0 test: ^1.0.0 + +dependency_overrides: + async: + git: + url: git://github.com/dart-lang/async.git + ref: null_safety + boolean_selector: + git: + url: git://github.com/dart-lang/boolean_selector.git + ref: null_safety + charcode: + git: + url: git://github.com/dart-lang/charcode.git + ref: null_safety + clock: + git: + url: git://github.com/dart-lang/clock.git + ref: null_safety + collection: 1.15.0-nullsafety + js: + git: + url: git://github.com/dart-lang/sdk.git + path: pkg/js + fake_async: + git: + url: git://github.com/dart-lang/fake_async.git + ref: null_safety + matcher: + git: + url: git://github.com/dart-lang/matcher.git + ref: null_safety + meta: 1.3.0-nullsafety + path: + git: + url: git://github.com/dart-lang/path.git + ref: null_safety + pedantic: + git: + url: git://github.com/dart-lang/pedantic.git + ref: null_safety + source_maps: + git: + url: git://github.com/dart-lang/source_maps.git + ref: null_safety + source_map_stack_trace: + git: + url: git://github.com/dart-lang/source_map_stack_trace.git + ref: null_safety + source_span: + git: + url: git://github.com/dart-lang/source_span.git + ref: null_safety + stack_trace: + git: + url: git://github.com/dart-lang/stack_trace.git + ref: null_safety + stream_channel: + git: + url: git://github.com/dart-lang/stream_channel.git + ref: null_safety + string_scanner: + git: + url: git://github.com/dart-lang/string_scanner.git + ref: null_safety + term_glyph: + git: + url: git://github.com/dart-lang/term_glyph.git + ref: null_safety + test_api: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test_api + test_core: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test_core + test: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test
diff --git a/test/pool_test.dart b/test/pool_test.dart index 091cd55..20e92d5 100644 --- a/test/pool_test.dart +++ b/test/pool_test.dart
@@ -438,7 +438,7 @@ }); group('forEach', () { - Pool pool; + late Pool pool; tearDown(() async { await pool.close(); @@ -609,7 +609,7 @@ delayedToString); // ignore: cancel_subscriptions - StreamSubscription subscription; + late StreamSubscription subscription; subscription = stream.listen( (data) { @@ -659,9 +659,9 @@ }); group('errors', () { - Future<void> errorInIterator( - {bool Function(int item, Object error, StackTrace stack) - onError}) async { + Future<void> errorInIterator({ + bool Function(int item, Object error, StackTrace stack)? onError, + }) async { pool = Pool(20); var listFuture = pool @@ -704,7 +704,8 @@ test('error in action, no onError', () async { pool = Pool(20); - var list = await pool.forEach(Iterable<int>.generate(100), (i) async { + var list = await pool.forEach(Iterable<int>.generate(100), + (int i) async { await Future.delayed(const Duration(milliseconds: 10)); if (i % 10 == 0) { throw UnsupportedError('Multiples of 10 not supported'); @@ -737,9 +738,7 @@ /// A matcher for Futures that asserts that they don't complete. /// /// This should only be called within a [FakeAsync.run] zone. -Matcher get doesNotComplete => predicate((future) { - expect(future, const TypeMatcher<Future>()); - +Matcher get doesNotComplete => predicate((Future future) { var stack = Trace.current(1); future.then((_) => registerException( TestFailure('Expected future not to complete.'), stack));