-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
wait_for.dart
180 lines (171 loc) · 7.49 KB
/
wait_for.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.cli;
/**
* Synchronously blocks the calling isolate to wait for asynchronous events to
* complete.
*
* If the [timeout] parameter is supplied, [waitForEvent] will return after
* the specified timeout even if no events have occurred.
*
* This call does the following:
* - suspends the current execution stack,
* - runs the microtask queue until it is empty,
* - waits until the message queue is not empty,
* - handles messages on the message queue, plus their associated microtasks,
* until the message queue is empty,
* - resumes the original stack.
*
* This function breaks the usual promise offered by Dart semantics that
* message handlers and microtasks run to completion before the next message
* handler or microtask begins to run. Of particular note is that use of this
* function in a finally block will allow microtasks and message handlers to
* run before all finally blocks for an exception have completed, possibly
* breaking invariants in your program.
*
* This function will synchronously throw the first unhandled exception it
* encounters in running the microtasks and message handlers as though the
* throwing microtask or message handler was the only Dart invocation on the
* stack. That is, unhandled exceptions in a microtask or message handler will
* skip over stacks suspended in a call to [waitForEvent].
*
* Calls to this function may be nested. Earlier invocations will not
* be able to complete until subsequent ones do. Messages that arrive after
* a subsequent invocation are "consumed" by that invocation, and do not
* unblock an earlier invocation. Please be aware that nesting calls to
* [waitForEvent] can lead to deadlock when subsequent calls block to wait for
* a condition that is only satisfied after an earlier call returns.
*
* Please note that this call is only available in the standalone command-line
* Dart VM. Further, because it suspends the current execution stack until the
* message queue is empty, even when running in the standalone command-line VM
* there exists a risk that the current execution stack will be starved.
*/
external void _waitForEvent(int timeoutMillis);
@pragma("vm:entry-point")
void Function(int) _getWaitForEvent() => _waitForEvent;
// This should be set from C++ code by the embedder to wire up waitFor() to the
// native implementation. In the standalone VM this is set to _waitForEvent()
// above. If it is null, calling waitFor() will throw an UnsupportedError.
@pragma("vm:entry-point")
void Function(int)? _waitForEventClosure;
class _WaitForUtils {
static void waitForEvent({Duration? timeout}) {
final closure = _waitForEventClosure;
if (closure == null) {
throw new UnsupportedError("waitFor is not supported by this embedder");
}
closure(timeout == null ? 0 : max(1, timeout.inMilliseconds));
}
}
/**
* Suspends the stack, runs microtasks, and handles incoming events until
* [future] completes.
*
* ## Deprecation notice
*
* The `waitFor` feature is deprecated.
* The feature was intended to solve a particular problem for existing code,
* a problem introduced by a breaking change to the platform libraries.
* The `waitFor` function is not suitable for general use.
* The feature has shortcomings that can affect other code
* running in the same isolate, including:
* * A function call that looks synchronous may cause other asynchronous
* events to run before it returns.
* This is something synchronous code can usually assume not to happen,
* and some code may have been written to take advantage of that
* assumed behavior. Such code can fail in unexpected ways.
* * Multiple nested calls to `waitFor` may block each other
* since the most recent call always needs to complete
* before any other call can complete.
* Judicious use of `waitFor` is necessary to avoid unexpected deadlocks
* which wouldn't happen if using `await` instead.
* If more than one library in the same program is using `waitFor`,
* then it's hard to avoid or control whether such blocking will happen.
*
* The feature is not actively maintained.
* It will remain as-is to support the original problem it was added to solve,
* at least until that problem can be solved in some other way.
*
* ## Call semantics
*
* This call does the following:
* - While [future] is not completed:
* - suspends the current execution stack,
* - runs the microtask queue until it is empty,
* - waits until the message queue is not empty,
* - handles messages on the message queue, plus their associated microtasks,
* until the message queue is empty,
* - resumes the original stack.
*
* This function breaks the usual promise offered by Dart semantics that
* message handlers and microtasks run to completion before the next message
* handler or microtask begins to run. Of particular note is that use of this
* function in a finally block will allow microtasks and message handlers to
* run before all finally blocks for an exception have completed, possibly
* breaking invariants in your program.
*
* Use of this function should be considered a last resort when it is not
* possible to convert a Dart program entirely to an asynchronous style using
* `async` and `await`.
*
* If the [Future] completes normally, its result is returned. If the [Future]
* completes with an error, the error and stack trace are wrapped in an
* [AsyncError] and thrown. If a microtask or message handler run during this
* call results in an unhandled exception, that exception will be propagated
* as though the microtask or message handler was the only Dart invocation on
* the stack. That is, unhandled exceptions in a microtask or message handler
* will skip over stacks suspended in a call to [waitFor].
*
* If the optional `timeout` parameter is passed, [waitFor] throws a
* [TimeoutException] if the [Future] is not completed within the specified
* period.
*
* Calls to [waitFor] may be nested. Earlier invocations will not complete
* until subsequent ones do, but the completion of a subsequent invocation will
* cause the previous invocation to wake up and check its [Future] for
* completion.
*
* Please be aware that nesting calls to [waitFor] can lead to deadlock if
* subsequent calls block waiting for a condition that is only satisfied when
* an earlier call returns.
*/
@Deprecated(
"This functionality is incomplete and may be removed in a later version")
T waitFor<T>(Future<T> future, {Duration? timeout}) {
late T result;
bool futureCompleted = false;
Object? error;
StackTrace? stacktrace;
future.then((T r) {
futureCompleted = true;
result = r;
}, onError: (e, st) {
error = e;
stacktrace = st;
});
late Stopwatch s;
if (timeout != null) {
s = new Stopwatch()..start();
}
Timer.run(() {}); // Ensure there is at least one message.
while (!futureCompleted && (error == null)) {
Duration? remaining;
if (timeout != null) {
if (s.elapsed >= timeout) {
throw new TimeoutException("waitFor() timed out", timeout);
}
remaining = timeout - s.elapsed;
}
_WaitForUtils.waitForEvent(timeout: remaining);
}
if (timeout != null) {
s.stop();
}
Timer.run(() {}); // Ensure that previous calls to waitFor are woken up.
if (error != null) {
throw new AsyncError(error!, stacktrace);
}
return result;
}