Skip to content

Commit aa34e70

Browse files
committed
Revert "Make Zone functions not call register*, but properly catch errors."
This reverts commit fc2c46a. Reason for revert: broke Flutter tests: https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084113/+/u/Run_framework_coverage_tests/stdout https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084097/+/u/Run_framework_tests_libraries_tests/stdout https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084081/+/u/Run_framework_tests_misc_tests/stdout https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084049/+/u/Run_framework_tests_widgets_tests/stdout https://logs.chromium.org/logs/dart/buildbucket/cr-buildbucket/8724303399273084033/+/u/Run_tool_tests_tests/stdout Original change's description: > Make Zone functions not call `register*`, but properly catch errors. > > Make `Zone.createTimer`, `Zone.createPeriodicTimer` and `Zone.scheduleMicrotask` > not call register on the same zone. > These are low-level functions that should not build on top > of other low-level functions. > > Instead the function is registered in the `Timer` constructors and > top-level `scheduleMicrotask` code, and the root zone's `createTimer` > and `scheduleMicrotask` just make sure that the callback will run > in its original zone, and that uncaught errors are handled in the > correct zone. > Avoids a double-registration that timers did. > > Significant clean-up. > - The private `_Zone`, `_ZoneSpecification` and `_ZoneDelegate` > subclasses are not necessary now that their superclasses are `final`. > - Stopped using type aliases instead of function types > and old-style function arguments. > - Renamed some parameters to not be, fx, `f`. > > Avoided some closure allocations for microtasks by putting the zone > into the queue entry, instead of wrapping the callback in a closure calling `zone.run`. > > This is a potentially visible change if any code uses the zone functions > directly and expect them to invoke `Zone.register*`. > (Suspiciously no test failed with this change. May need to add some.) > > Fixes #59913. > > CoreLibraryReviewExempt: Have had all +1s, just not at the same time. > Bug: https://dartbug.com/59913 > Change-Id: Ie3c87f5f22aedcbe30ab04718df98e1c0f0659c3 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405020 > Reviewed-by: Martin Kustermann <[email protected]> > Commit-Queue: Lasse Nielsen <[email protected]> Bug: https://dartbug.com/59913 Change-Id: I386b2b6d2594a50a7597363e13fe24111ea72ade Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406685 Bot-Commit: Rubber Stamper <[email protected]> Reviewed-by: Jake Macdonald <[email protected]>
1 parent 126af93 commit aa34e70

File tree

11 files changed

+728
-1252
lines changed

11 files changed

+728
-1252
lines changed

CHANGELOG.md

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,6 @@
44

55
### Libraries
66

7-
#### `dart:async`
8-
9-
- The `Timer` and `Timer.periodic` constructors and the top-level
10-
`scheduleMicrotask` function no longer *bind* and *guard* their callback
11-
in the current zone, using `Zone.bindCallbackGuarded` and
12-
`Zone.bindUnaryCallbackGuarded`. They only registers the callback
13-
before calling the zone's `Zone.createTimer`, `Zone.createPeriodicTimer`
14-
or `Zone.scheduleMicrotask` with the registered function.
15-
- The default `Zone.createTimer` and `Zone.createPeriodicTimer`,
16-
no longer registers the callback in the current zone, which would
17-
be a second registration if called through `Timer` or `Timer.periodic`.
18-
- The default (root) zone's `Zone.createTimer`, `Zone.createPeriodicTimer`
19-
and `Zone.scheduleMicrotask` now always run the callback using
20-
`Zone.run`/`Zone.runUnary`, and catch and reports errors from that
21-
to the same zone, rather than relying on the callback
22-
having been bound (and guarded) in the zone.
23-
- Together these changes ensure that if a custom zone's
24-
`Zone.registerCallback` (or similar for a unary or binary function)
25-
returns a new function which can throw, that thrown error is reported
26-
in the correct error zone.
27-
287
#### `dart:core`
298

309
- Added `Iterable.withIterator` constructor.

pkg/front_end/testcases/nnbd/return_async.dart.strong.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static method allYield3() → asy::Future<void> async /* emittedValueType= void
2121
self::throwSync();
2222
}
2323
static method customErrorZone() → asy::Future<void> async /* emittedValueType= void */ {
24-
final asy::Completer<void> completer = new asy::_AsyncCompleter::•<void>();
24+
final asy::Completer<void> completer = asy::Completer::•<void>();
2525
asy::runZonedGuarded<asy::Future<Null>>(() → asy::Future<Null> async /* emittedValueType= Null */ {
2626
await self::allYield();
2727
completer.{asy::Completer::complete}(null){([FutureOr<void>?]) → void};

pkg/front_end/testcases/nnbd/return_async.dart.strong.modular.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static method allYield3() → asy::Future<void> async /* emittedValueType= void
2121
self::throwSync();
2222
}
2323
static method customErrorZone() → asy::Future<void> async /* emittedValueType= void */ {
24-
final asy::Completer<void> completer = new asy::_AsyncCompleter::•<void>();
24+
final asy::Completer<void> completer = asy::Completer::•<void>();
2525
asy::runZonedGuarded<asy::Future<Null>>(() → asy::Future<Null> async /* emittedValueType= Null */ {
2626
await self::allYield();
2727
completer.{asy::Completer::complete}(null){([FutureOr<void>?]) → void};

pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static method allYield3() → asy::Future<void> async /* emittedValueType= void
2121
self::throwSync();
2222
}
2323
static method customErrorZone() → asy::Future<void> async /* emittedValueType= void */ {
24-
final asy::Completer<void> completer = new asy::_AsyncCompleter::•<void>();
24+
final asy::Completer<void> completer = asy::Completer::•<void>();
2525
asy::runZonedGuarded<asy::Future<Null>>(() → asy::Future<Null> async /* emittedValueType= Null */ {
2626
await self::allYield();
2727
completer.{asy::Completer::complete}(null){([FutureOr<void>?]) → void};

sdk/lib/async/future.dart

Lines changed: 36 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ abstract interface class Future<T> {
235235
static final _Future<void> _nullFuture = nullFuture as _Future<void>;
236236

237237
/// A `Future<bool>` completed with `false`.
238-
static final _Future<bool> _falseFuture = _Future<bool>.zoneValue(
238+
static final _Future<bool> _falseFuture = new _Future<bool>.zoneValue(
239239
false,
240240
_rootZone,
241241
);
@@ -253,7 +253,7 @@ abstract interface class Future<T> {
253253
/// If a non-future value is returned, the returned future is completed
254254
/// with that value.
255255
factory Future(FutureOr<T> computation()) {
256-
_Future<T> result = _Future<T>();
256+
_Future<T> result = new _Future<T>();
257257
Timer.run(() {
258258
FutureOr<T> computationResult;
259259
try {
@@ -280,7 +280,7 @@ abstract interface class Future<T> {
280280
/// If calling [computation] returns a non-future value,
281281
/// the returned future is completed with that value.
282282
factory Future.microtask(FutureOr<T> computation()) {
283-
_Future<T> result = _Future<T>();
283+
_Future<T> result = new _Future<T>();
284284
scheduleMicrotask(() {
285285
FutureOr<T> computationResult;
286286
try {
@@ -314,7 +314,7 @@ abstract interface class Future<T> {
314314
try {
315315
result = computation();
316316
} catch (error, stackTrace) {
317-
var future = _Future<T>();
317+
var future = new _Future<T>();
318318
_asyncCompleteWithErrorCallback(future, error, stackTrace);
319319
return future;
320320
}
@@ -349,7 +349,7 @@ abstract interface class Future<T> {
349349
@pragma("vm:entry-point")
350350
@pragma("vm:prefer-inline")
351351
factory Future.value([FutureOr<T>? value]) {
352-
return _Future<T>.immediate(value == null ? value as T : value);
352+
return new _Future<T>.immediate(value == null ? value as T : value);
353353
}
354354

355355
/// Creates a future that completes with an error.
@@ -371,7 +371,7 @@ abstract interface class Future<T> {
371371
/// ```
372372
factory Future.error(Object error, [StackTrace? stackTrace]) {
373373
AsyncError(:error, :stackTrace) = _interceptUserError(error, stackTrace);
374-
return _Future<T>.immediateError(error, stackTrace);
374+
return new _Future<T>.immediateError(error, stackTrace);
375375
}
376376

377377
/// Creates a future that runs its computation after a delay.
@@ -390,7 +390,7 @@ abstract interface class Future<T> {
390390
/// If [computation] is omitted,
391391
/// it will be treated as if [computation] was `() => null`,
392392
/// and the future will eventually complete with the `null` value.
393-
/// In that case, [T] **must** be nullable.
393+
/// In that case, [T] must be nullable.
394394
///
395395
/// If calling [computation] throws, the created future will complete with the
396396
/// error.
@@ -405,32 +405,28 @@ abstract interface class Future<T> {
405405
/// });
406406
/// ```
407407
factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) {
408+
if (computation == null && !typeAcceptsNull<T>()) {
409+
throw ArgumentError.value(
410+
null,
411+
"computation",
412+
"The type parameter is not nullable",
413+
);
414+
}
408415
_Future<T> result = _Future<T>();
409-
if (computation == null) {
410-
const T? defaultResult = null;
411-
if (defaultResult is T) {
412-
result._zone.createTimer(duration, () {
413-
result._complete(defaultResult);
414-
});
416+
new Timer(duration, () {
417+
if (computation == null) {
418+
result._complete(null as T);
415419
} else {
416-
throw ArgumentError(
417-
"Required when the type parameter is not nullable",
418-
"computation",
419-
);
420-
}
421-
} else {
422-
var registeredComputation = result._zone.registerCallback(computation);
423-
result._zone.createTimer(duration, () {
424420
FutureOr<T> computationResult;
425421
try {
426-
computationResult = result._zone.run(registeredComputation);
422+
computationResult = computation();
427423
} catch (e, s) {
428424
_completeWithErrorCallback(result, e, s);
429425
return;
430426
}
431427
result._complete(computationResult);
432-
});
433-
}
428+
}
429+
});
434430
return result;
435431
}
436432

@@ -708,63 +704,31 @@ abstract interface class Future<T> {
708704
/// // Outputs: 'Finished with 3'
709705
/// ```
710706
static Future<void> doWhile(FutureOr<bool> action()) {
711-
var zone = Zone.current;
712-
_Future<void> doneSignal = _Future<void>.zone(zone);
713-
714-
/// As a function instead of a tear-off because some compilers
715-
/// prefer that. (Same overhead for creating, but tear-offs have
716-
/// more complicated equality, which isn't used anyway.)
717-
void completeError(Object e, StackTrace s) {
718-
doneSignal._completeError(e, s);
719-
}
720-
721-
// Bind/register the callbacks only once, and reuse them on every iteration.
722-
// This avoids, e.g., deeply nested stack traces from the `stack_trace`
707+
_Future<void> doneSignal = new _Future<void>();
708+
late void Function(bool) nextIteration;
709+
// Bind this callback explicitly so that each iteration isn't bound in the
710+
// context of all the previous iterations' callbacks.
711+
// This avoids, e.g., deeply nested stack traces from the stack trace
723712
// package.
724-
void Function(bool)? registeredNextIteration;
725-
void Function(Object, StackTrace)? registeredCompleteError;
726-
727-
/// True until the first asynchronous gap.
728-
bool sync = true;
729-
void nextIteration(bool keepGoing) {
713+
nextIteration = Zone.current.bindUnaryCallbackGuarded((bool keepGoing) {
730714
while (keepGoing) {
731715
FutureOr<bool> result;
732716
try {
733717
result = action();
734718
} catch (error, stackTrace) {
735-
if (sync) {
736-
// Complete asynchronously if still running synchronously.
737-
_asyncCompleteWithErrorCallback(doneSignal, error, stackTrace);
738-
} else {
739-
_completeWithErrorCallback(doneSignal, error, stackTrace);
740-
}
719+
// Cannot use _completeWithErrorCallback because it completes
720+
// the future synchronously.
721+
_asyncCompleteWithErrorCallback(doneSignal, error, stackTrace);
741722
return;
742723
}
743724
if (result is Future<bool>) {
744-
if (result is _Future<bool>) {
745-
var onValue =
746-
registeredNextIteration ??= zone.registerUnaryCallback(
747-
nextIteration,
748-
);
749-
var onError =
750-
registeredCompleteError ??= zone.registerBinaryCallback(
751-
completeError,
752-
);
753-
// Reuse registered callbacks when it's a `_Future`.
754-
result._addThenListener(zone, onValue, onError);
755-
} else {
756-
// Let `then` do callback registration for non-native futures.
757-
// It will do that anyway.
758-
result.then(nextIteration, onError: completeError);
759-
}
760-
sync = false;
725+
result.then(nextIteration, onError: doneSignal._completeError);
761726
return;
762727
}
763728
keepGoing = result;
764729
}
765730
doneSignal._complete(null);
766-
}
767-
731+
});
768732
nextIteration(true);
769733
return doneSignal;
770734
}
@@ -934,7 +898,7 @@ abstract interface class Future<T> {
934898

935899
/// Stop waiting for this future after [timeLimit] has passed.
936900
///
937-
/// Creates a _timeout future_ that completes
901+
/// Creates a new _timeout future_ that completes
938902
/// with the same result as this future, the _source future_,
939903
/// *if* the source future completes in time.
940904
///
@@ -1195,7 +1159,7 @@ class TimeoutException implements Exception {
11951159
/// one — you can use a Completer as follows:
11961160
/// ```dart
11971161
/// class AsyncOperation {
1198-
/// final Completer _completer = Completer();
1162+
/// final Completer _completer = new Completer();
11991163
///
12001164
/// Future<T> doOperation() {
12011165
/// _startOperation();
@@ -1234,7 +1198,7 @@ abstract interface class Completer<T> {
12341198
/// completer.complete('completion value');
12351199
/// }
12361200
/// ```
1237-
factory Completer() = _AsyncCompleter<T>;
1201+
factory Completer() => new _AsyncCompleter<T>();
12381202

12391203
/// Completes the future synchronously.
12401204
///
@@ -1285,7 +1249,7 @@ abstract interface class Completer<T> {
12851249
/// foo(); // In this case, foo() runs after bar().
12861250
/// });
12871251
/// ```
1288-
factory Completer.sync() = _SyncCompleter<T>;
1252+
factory Completer.sync() => new _SyncCompleter<T>();
12891253

12901254
/// The future that is completed by this completer.
12911255
///
@@ -1367,7 +1331,7 @@ abstract interface class Completer<T> {
13671331
bool get isCompleted;
13681332
}
13691333

1370-
// Helper function completing a `_Future` with error, but checking the zone
1334+
// Helper function completing a _Future with error, but checking the zone
13711335
// for error replacement and missing stack trace first.
13721336
// Only used for errors that are *caught*.
13731337
// A user provided error object should use `_interceptUserError` which

0 commit comments

Comments
 (0)