From c5ad1067b75ccb5faaa2d23e6ceef6c061a2c7ca Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Sun, 23 Dec 2018 15:20:45 -0800 Subject: [PATCH] Handle errors in `compute()` by propagating them to the Future. (#24848) --- .../flutter/lib/src/foundation/isolates.dart | 28 +++++++++++++++---- .../test/foundation/isolates_test.dart | 22 +++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 packages/flutter/test/foundation/isolates_test.dart diff --git a/packages/flutter/lib/src/foundation/isolates.dart b/packages/flutter/lib/src/foundation/isolates.dart index 7eaf6181b0..fd6ee57a2c 100644 --- a/packages/flutter/lib/src/foundation/isolates.dart +++ b/packages/flutter/lib/src/foundation/isolates.dart @@ -49,6 +49,7 @@ Future compute(ComputeCallback callback, Q message, { String debu final Flow flow = Flow.begin(); Timeline.startSync('$debugLabel: start', flow: flow); final ReceivePort resultPort = ReceivePort(); + final ReceivePort errorPort = ReceivePort(); Timeline.finishSync(); final Isolate isolate = await Isolate.spawn<_IsolateConfiguration>( _spawn, @@ -61,13 +62,32 @@ Future compute(ComputeCallback callback, Q message, { String debu ), errorsAreFatal: true, onExit: resultPort.sendPort, + onError: errorPort.sendPort, ); - final R result = await resultPort.first; + final Completer result = Completer(); + errorPort.listen((dynamic errorData) { + assert(errorData is List); + assert(errorData.length == 2); + final Exception exception = Exception(errorData[0]); + final StackTrace stack = StackTrace.fromString(errorData[1]); + if (result.isCompleted) { + Zone.current.handleUncaughtError(exception, stack); + } else { + result.completeError(exception, stack); + } + }); + resultPort.listen((dynamic resultData) { + assert(resultData == null || resultData is R); + if (!result.isCompleted) + result.complete(resultData); + }); + await result.future; Timeline.startSync('$debugLabel: end', flow: Flow.end(flow.id)); resultPort.close(); + errorPort.close(); isolate.kill(); Timeline.finishSync(); - return result; + return result.future; } @immutable @@ -92,9 +112,7 @@ void _spawn(_IsolateConfiguration configuration) { R result; Timeline.timeSync( '${configuration.debugLabel}', - () { - result = configuration.apply(); - }, + () { result = configuration.apply(); }, flow: Flow.step(configuration.flowId), ); Timeline.timeSync( diff --git a/packages/flutter/test/foundation/isolates_test.dart b/packages/flutter/test/foundation/isolates_test.dart new file mode 100644 index 0000000000..88dabcebcf --- /dev/null +++ b/packages/flutter/test/foundation/isolates_test.dart @@ -0,0 +1,22 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +import '../flutter_test_alternative.dart'; + +int test1(int value) { + return value + 1; +} + +int test2(int value) { + throw 2; +} + +void main() { + test('compute()', () async { + expect(await compute(test1, 0), 1); + expect(compute(test2, 0), throwsA(isInstanceOf())); + }); +}