Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fc2c46a

Browse files
lrhnCommit Queue
authored and
Commit Queue
committed
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 <kustermann@google.com> Commit-Queue: Lasse Nielsen <lrn@google.com>
1 parent f797e5f commit fc2c46a

File tree

11 files changed

+1252
-728
lines changed

11 files changed

+1252
-728
lines changed

‎CHANGELOG.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@
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+
728
#### `dart:core`
829

930
- 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 = asy::Completer::•<void>();
24+
final asy::Completer<void> completer = new asy::_AsyncCompleter::•<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 = asy::Completer::•<void>();
24+
final asy::Completer<void> completer = new asy::_AsyncCompleter::•<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 = asy::Completer::•<void>();
24+
final asy::Completer<void> completer = new asy::_AsyncCompleter::•<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: 72 additions & 36 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 = new_Future<bool>.zoneValue(
238+
static final _Future<bool> _falseFuture = _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 = new_Future<T>();
256+
_Future<T> result = _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 = new_Future<T>();
283+
_Future<T> result = _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 = new_Future<T>();
317+
var future = _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 new_Future<T>.immediate(value == null ? value as T : value);
352+
return _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 new_Future<T>.immediateError(error, stackTrace);
374+
return _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,28 +405,32 @@ 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-
}
415408
_Future<T> result = _Future<T>();
416-
new Timer(duration, () {
417-
if (computation == null) {
418-
result._complete(null as 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+
});
419415
} 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, () {
420424
FutureOr<T> computationResult;
421425
try {
422-
computationResult = computation();
426+
computationResult = result._zone.run(registeredComputation);
423427
} catch (e, s) {
424428
_completeWithErrorCallback(result, e, s);
425429
return;
426430
}
427431
result._complete(computationResult);
428-
}
429-
});
432+
});
433+
}
430434
return result;
431435
}
432436

@@ -704,31 +708,63 @@ abstract interface class Future<T> {
704708
/// // Outputs: 'Finished with 3'
705709
/// ```
706710
static Future<void> doWhile(FutureOr<bool> action()) {
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
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`
712723
// package.
713-
nextIteration = Zone.current.bindUnaryCallbackGuarded((bool keepGoing) {
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) {
714730
while (keepGoing) {
715731
FutureOr<bool> result;
716732
try {
717733
result = action();
718734
} catch (error, stackTrace) {
719-
// Cannot use _completeWithErrorCallback because it completes
720-
// the future synchronously.
721-
_asyncCompleteWithErrorCallback(doneSignal, 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+
}
722741
return;
723742
}
724743
if (result is Future<bool>) {
725-
result.then(nextIteration, onError: doneSignal._completeError);
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;
726761
return;
727762
}
728763
keepGoing = result;
729764
}
730765
doneSignal._complete(null);
731-
});
766+
}
767+
732768
nextIteration(true);
733769
return doneSignal;
734770
}
@@ -898,7 +934,7 @@ abstract interface class Future<T> {
898934

899935
/// Stop waiting for this future after [timeLimit] has passed.
900936
///
901-
/// Creates a new _timeout future_ that completes
937+
/// Creates a _timeout future_ that completes
902938
/// with the same result as this future, the _source future_,
903939
/// *if* the source future completes in time.
904940
///
@@ -1159,7 +1195,7 @@ class TimeoutException implements Exception {
11591195
/// one — you can use a Completer as follows:
11601196
/// ```dart
11611197
/// class AsyncOperation {
1162-
/// final Completer _completer = new Completer();
1198+
/// final Completer _completer = Completer();
11631199
///
11641200
/// Future<T> doOperation() {
11651201
/// _startOperation();
@@ -1198,7 +1234,7 @@ abstract interface class Completer<T> {
11981234
/// completer.complete('completion value');
11991235
/// }
12001236
/// ```
1201-
factory Completer() =>new_AsyncCompleter<T>();
1237+
factory Completer() =_AsyncCompleter<T>;
12021238

12031239
/// Completes the future synchronously.
12041240
///
@@ -1249,7 +1285,7 @@ abstract interface class Completer<T> {
12491285
/// foo(); // In this case, foo() runs after bar().
12501286
/// });
12511287
/// ```
1252-
factory Completer.sync() =>new_SyncCompleter<T>();
1288+
factory Completer.sync() =_SyncCompleter<T>;
12531289

12541290
/// The future that is completed by this completer.
12551291
///
@@ -1331,7 +1367,7 @@ abstract interface class Completer<T> {
13311367
bool get isCompleted;
13321368
}
13331369

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

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /