Skip to content

Commit

Permalink
[VM] Adds Future.timeout stack unwinding support for --lazy-async-sta…
Browse files Browse the repository at this point in the history
…cks.

Bug: #40815
Change-Id: I1603e1effe67b727d5dc47c0830b4758c764cf4a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152328
Commit-Queue: Clement Skau <cskau@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
  • Loading branch information
Clement Skau authored and commit-bot@chromium.org committed Jul 8, 2020
1 parent 05003ab commit 219935a
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 13 deletions.
101 changes: 101 additions & 0 deletions runtime/tests/vm/dart/causal_stacks/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ Future<void> customErrorZone() async {
return completer.future;
}

// ----
// Scenario: Future.timeout:
// ----

Future awaitTimeout() async {
await (throwAsync().timeout(Duration(seconds: 1)));
}

// Helpers:

// We want lines that either start with a frame index or an async gap marker.
Expand Down Expand Up @@ -587,6 +595,7 @@ Future<void> doTestsCausal([String? debugInfoFilename]) async {
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
],
debugInfoFilename);

final customErrorZoneExpected = const <String>[
r'#0 throwSync \(.*/utils.dart:16(:3)?\)$',
r'#1 allYield3 \(.*/utils.dart:39(:3)?\)$',
Expand Down Expand Up @@ -638,6 +647,48 @@ Future<void> doTestsCausal([String? debugInfoFilename]) async {
r'#14 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);

final awaitTimeoutExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^^<asynchronous suspension>$',
r'^#1 awaitTimeout ',
];
await doTestAwait(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#2 doTestAwait ',
r'^#3 doTestsCausal ',
r'^<asynchronous suspension>$',
r'^#4 main \(.+\)$',
r'^#5 _startIsolate.<anonymous closure> ',
r'^#6 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);
await doTestAwaitThen(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#2 doTestAwaitThen ',
r'^#3 doTestsCausal ',
r'^<asynchronous suspension>$',
r'^#4 main \(.+\)$',
r'^#5 _startIsolate.<anonymous closure> ',
r'^#6 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#2 doTestAwaitCatchError ',
r'^#3 doTestsCausal ',
r'^<asynchronous suspension>$',
r'^#4 main \(.+\)$',
r'^#5 _startIsolate.<anonymous closure> ',
r'^#6 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);
}

// For: --no-causal-async-stacks --no-lazy-async-stacks
Expand Down Expand Up @@ -957,6 +1008,25 @@ Future<void> doTestsNoCausalNoLazy([String? debugInfoFilename]) async {
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitCatchError(
customErrorZone, customErrorZoneExpected, debugInfoFilename);

final awaitTimeoutExpected = const <String>[
r'#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^#1 _RootZone.runUnary ',
r'^#2 _FutureListener.handleValue ',
r'^#3 Future._propagateToListeners.handleValueCallback ',
r'^#4 Future._propagateToListeners ',
r'^#5 Future.(_addListener|_prependListeners).<anonymous closure> ',
r'^#6 _microtaskLoop ',
r'^#7 _startMicrotaskLoop ',
r'^#8 _runPendingImmediateCallback ',
r'^#9 _RawReceivePortImpl._handleMessage ',
];
await doTestAwait(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
await doTestAwaitThen(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
}

// For: --lazy-async-stacks
Expand Down Expand Up @@ -1218,4 +1288,35 @@ Future<void> doTestsLazy([String? debugInfoFilename]) async {
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitCatchError(
customErrorZone, customErrorZoneExpected, debugInfoFilename);

final awaitTimeoutExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 Future.timeout.<anonymous closure> \(dart:async/future_impl.dart\)$',
r'^<asynchronous suspension>$',
r'^#2 awaitTimeout ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwaitThen.<anonymous closure> ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
}
101 changes: 101 additions & 0 deletions runtime/tests/vm/dart_2/causal_stacks/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ Future<void> customErrorZone() async {
return completer.future;
}

// ----
// Scenario: Future.timeout:
// ----

Future awaitTimeout() async {
await (throwAsync().timeout(Duration(seconds: 1)));
}

// Helpers:

// We want lines that either start with a frame index or an async gap marker.
Expand Down Expand Up @@ -587,6 +595,7 @@ Future<void> doTestsCausal([String debugInfoFilename]) async {
r'^#7 _RawReceivePortImpl._handleMessage \(.+\)$',
],
debugInfoFilename);

final customErrorZoneExpected = const <String>[
r'#0 throwSync \(.*/utils.dart:16(:3)?\)$',
r'#1 allYield3 \(.*/utils.dart:39(:3)?\)$',
Expand Down Expand Up @@ -638,6 +647,48 @@ Future<void> doTestsCausal([String debugInfoFilename]) async {
r'#14 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);

final awaitTimeoutExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^^<asynchronous suspension>$',
r'^#1 awaitTimeout ',
];
await doTestAwait(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#2 doTestAwait ',
r'^#3 doTestsCausal ',
r'^<asynchronous suspension>$',
r'^#4 main \(.+\)$',
r'^#5 _startIsolate.<anonymous closure> ',
r'^#6 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);
await doTestAwaitThen(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#2 doTestAwaitThen ',
r'^#3 doTestsCausal ',
r'^<asynchronous suspension>$',
r'^#4 main \(.+\)$',
r'^#5 _startIsolate.<anonymous closure> ',
r'^#6 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#2 doTestAwaitCatchError ',
r'^#3 doTestsCausal ',
r'^<asynchronous suspension>$',
r'^#4 main \(.+\)$',
r'^#5 _startIsolate.<anonymous closure> ',
r'^#6 _RawReceivePortImpl._handleMessage ',
],
debugInfoFilename);
}

// For: --no-causal-async-stacks --no-lazy-async-stacks
Expand Down Expand Up @@ -957,6 +1008,25 @@ Future<void> doTestsNoCausalNoLazy([String debugInfoFilename]) async {
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitCatchError(
customErrorZone, customErrorZoneExpected, debugInfoFilename);

final awaitTimeoutExpected = const <String>[
r'#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^#1 _RootZone.runUnary ',
r'^#2 _FutureListener.handleValue ',
r'^#3 Future._propagateToListeners.handleValueCallback ',
r'^#4 Future._propagateToListeners ',
r'^#5 Future.(_addListener|_prependListeners).<anonymous closure> ',
r'^#6 _microtaskLoop ',
r'^#7 _startMicrotaskLoop ',
r'^#8 _runPendingImmediateCallback ',
r'^#9 _RawReceivePortImpl._handleMessage ',
];
await doTestAwait(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
await doTestAwaitThen(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
}

// For: --lazy-async-stacks
Expand Down Expand Up @@ -1218,4 +1288,35 @@ Future<void> doTestsLazy([String debugInfoFilename]) async {
customErrorZone, customErrorZoneExpected, debugInfoFilename);
await doTestAwaitCatchError(
customErrorZone, customErrorZoneExpected, debugInfoFilename);

final awaitTimeoutExpected = const <String>[
r'^#0 throwAsync \(.*/utils.dart:21(:3)?\)$',
r'^<asynchronous suspension>$',
r'^#1 Future.timeout.<anonymous closure> \(dart:async/future_impl.dart\)$',
r'^<asynchronous suspension>$',
r'^#2 awaitTimeout ',
r'^<asynchronous suspension>$',
];
await doTestAwait(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwait ',
r'^<asynchronous suspension>$',
r'^#4 doTestsLazy ',
r'^<asynchronous suspension>$',
r'^#5 main ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitThen(
awaitTimeout,
awaitTimeoutExpected +
const <String>[
r'^#3 doTestAwaitThen.<anonymous closure> ',
r'^<asynchronous suspension>$',
],
debugInfoFilename);
await doTestAwaitCatchError(
awaitTimeout, awaitTimeoutExpected + const <String>[], debugInfoFilename);
}
16 changes: 12 additions & 4 deletions runtime/vm/compiler/frontend/scope_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,8 @@ void ScopeBuilder::VisitFunctionNode() {
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);

const auto& function = parsed_function_->function();

intptr_t list_length =
helper_.ReadListLength(); // read type_parameters list length.
for (intptr_t i = 0; i < list_length; ++i) {
Expand All @@ -565,10 +567,8 @@ void ScopeBuilder::VisitFunctionNode() {
}

if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
intptr_t offset = parsed_function_->function().num_fixed_parameters();
for (intptr_t i = 0;
i < parsed_function_->function().NumOptionalPositionalParameters();
i++) {
intptr_t offset = function.num_fixed_parameters();
for (intptr_t i = 0; i < function.NumOptionalPositionalParameters(); i++) {
parsed_function_->ParameterVariable(offset + i)->set_is_forced_stack();
}
}
Expand Down Expand Up @@ -627,6 +627,14 @@ void ScopeBuilder::VisitFunctionNode() {
}
}
}

// Mark known chained futures such as _Future::timeout()'s _future.
if (function.recognized_kind() == MethodRecognizer::kFutureTimeout &&
depth_.function_ == 1) {
LocalVariable* future = scope_->LookupVariable(Symbols::_future(), true);
ASSERT(future != nullptr);
future->set_is_chained_future();
}
}

void ScopeBuilder::VisitInitializer() {
Expand Down
1 change: 1 addition & 0 deletions runtime/vm/compiler/recognized_methods_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ namespace dart {
V(Pointer, get:address, FfiGetAddress, 0x29a505a1) \
V(::, reachabilityFence, ReachabilityFence, 0x0) \
V(_Utf8Decoder, _scan, Utf8DecoderScan, 0x78f44c3c) \
V(_Future, timeout, FutureTimeout, 0) \

// List of intrinsics:
// (class-name, function-name, intrinsification method, fingerprint).
Expand Down
1 change: 1 addition & 0 deletions runtime/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -6708,6 +6708,7 @@ class Context : public Object {
static const intptr_t kAwaitJumpVarIndex = 0;
static const intptr_t kAsyncCompleterIndex = 1;
static const intptr_t kControllerIndex = 1;
static const intptr_t kChainedFutureIndex = 2;

static intptr_t variable_offset(intptr_t context_index) {
return OFFSET_OF_RETURNED_VALUE(ContextLayout, data) +
Expand Down
10 changes: 9 additions & 1 deletion runtime/vm/scopes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
LocalVariable* await_jump_var = nullptr;
LocalVariable* async_completer = nullptr;
LocalVariable* controller = nullptr;
LocalVariable* chained_future = nullptr;
for (intptr_t i = 0; i < num_variables(); i++) {
LocalVariable* variable = VariableAt(i);
if (variable->owner() == this) {
Expand All @@ -222,6 +223,8 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
async_completer = variable;
} else if (variable->name().Equals(Symbols::Controller())) {
controller = variable;
} else if (variable->is_chained_future()) {
chained_future = variable;
}
}
}
Expand All @@ -243,6 +246,11 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
*found_captured_variables = true;
ASSERT(controller->index().value() == Context::kControllerIndex);
}
if (chained_future != nullptr) {
AllocateContextVariable(chained_future, &context_owner);
*found_captured_variables = true;
ASSERT(chained_future->index().value() == Context::kChainedFutureIndex);
}

while (pos < num_parameters) {
LocalVariable* parameter = VariableAt(pos);
Expand Down Expand Up @@ -273,7 +281,7 @@ VariableIndex LocalScope::AllocateVariables(VariableIndex first_parameter_index,
if (variable->is_captured()) {
// Skip the two variables already pre-allocated above.
if (variable != await_jump_var && variable != async_completer &&
variable != controller) {
variable != controller && variable != chained_future) {
AllocateContextVariable(variable, &context_owner);
*found_captured_variables = true;
}
Expand Down
5 changes: 5 additions & 0 deletions runtime/vm/scopes.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class LocalVariable : public ZoneAllocated {
is_forced_stack_(false),
is_explicit_covariant_parameter_(false),
is_late_(false),
is_chained_future_(false),
late_init_offset_(0),
type_check_mode_(kDoTypeCheck),
index_() {
Expand Down Expand Up @@ -131,6 +132,9 @@ class LocalVariable : public ZoneAllocated {
bool is_late() const { return is_late_; }
void set_is_late() { is_late_ = true; }

bool is_chained_future() const { return is_chained_future_; }
void set_is_chained_future() { is_chained_future_ = true; }

intptr_t late_init_offset() const { return late_init_offset_; }
void set_late_init_offset(intptr_t late_init_offset) {
late_init_offset_ = late_init_offset;
Expand Down Expand Up @@ -220,6 +224,7 @@ class LocalVariable : public ZoneAllocated {
bool is_forced_stack_;
bool is_explicit_covariant_parameter_;
bool is_late_;
bool is_chained_future_;
intptr_t late_init_offset_;
TypeCheckMode type_check_mode_;
VariableIndex index_;
Expand Down
Loading

0 comments on commit 219935a

Please sign in to comment.