Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(182)

Side by Side Diff: packages/isolate/lib/isolate_runner.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « packages/isolate/lib/isolate.dart ('k') | packages/isolate/lib/load_balancer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library isolate.isolate_runner;
6
7 import "dart:async";
8 import "dart:isolate";
9
10 import 'ports.dart';
11 import 'runner.dart';
12 import 'src/lists.dart';
13
14 // Command tags. Shared between IsolateRunner and IsolateRunnerRemote.
15 const int _SHUTDOWN = 0;
16 const int _RUN = 1;
17
18 /// An easier to use interface on top of an [Isolate].
19 ///
20 /// Wraps an `Isolate` and allows pausing, killing and inspecting
21 /// the isolate more conveniently than the raw `Isolate` methods.
22 ///
23 /// Also allows running simple functions in the other isolate, and get back
24 /// the result.
25 class IsolateRunner implements Runner {
26 /// The underlying [Isolate] object of the isolate being controlled.
27 final Isolate isolate;
28
29 /// Command port for the [IsolateRunnerRemote].
30 final SendPort _commandPort;
31
32 /// Future returned by [onExit]. Set when [onExit] is first read.
33 Future _onExitFuture;
34
35 /// Create an [IsolateRunner] wrapper for [isolate]
36 ///
37 /// The preferred way to create an `IsolateRunner` is to use [spawn]
38 /// to create a new isolate and a runner for it.
39 ///
40 /// This constructor allows creating a runner for an already existing
41 /// isolate.
42 /// The [commandPort] must be the [IsolateRunnerRemote.commandPort] of
43 /// a remote running in that isolate.
44 IsolateRunner(this.isolate, SendPort commandPort)
45 : _commandPort = commandPort;
46
47 /// Create a new [Isolate], as by [Isolate.spawn] and wrap that.
48 ///
49 /// The returned [IsolateRunner] forwards operations to the new isolate,
50 /// and keeps a port open in the new isolate that receives commands
51 /// from the `IsolateRunner`. Remember to [close] the `IsolateRunner` when
52 /// it's no longer needed.
53 ///
54 /// The created isolate is set to have errors not be fatal.
55 static Future<IsolateRunner> spawn() async {
56 var channel = new SingleResponseChannel();
57 var isolate = await Isolate.spawn(IsolateRunnerRemote._create,
58 channel.port);
59 // The runner can be used to run multiple independent functions.
60 // An accidentally uncaught error shouldn't ruin it for everybody else.
61 isolate.setErrorsFatal(false);
62 var pingChannel = new SingleResponseChannel();
63 isolate.ping(pingChannel.port);
64 var commandPort = await channel.result;
65 var result = new IsolateRunner(isolate, commandPort);
66 // Guarantees that setErrorsFatal has completed.
67 await pingChannel.result;
68 return result;
69 }
70
71 /// Closes the `IsolateRunner` communication down.
72 ///
73 /// If the isolate isn't running something else to keep it alive,
74 /// this will also make the isolate shut down.
75 ///
76 /// Can be used to create an isolate, use [run] to start a service, and
77 /// then drop the connection and let the service control the isolate's
78 /// life cycle.
79 Future close() {
80 var channel = new SingleResponseChannel();
81 _commandPort.send(list2(_SHUTDOWN, channel.port));
82 return channel.result;
83 }
84
85 /// Kills the isolate.
86 ///
87 /// Starts by calling [close], but if that doesn't cause the isolate to
88 /// shut down in a timely manner, as given by [timeout], it follows up
89 /// with [Isolate.kill], with increasing urgency if necessary.
90 ///
91 /// If [timeout] is a zero duration, it goes directly to the most urgent
92 /// kill.
93 ///
94 /// If the isolate is already dead, the returned future will not complete.
95 /// If that may be the case, use [Future.timeout] on the returned future
96 /// to take extra action after a while. Example:
97 ///
98 /// var f = isolate.kill();
99 /// f.then((_) => print('Dead')
100 /// .timeout(new Duration(...), onTimeout: () => print('No response'));
101 Future kill({Duration timeout: const Duration(seconds: 1)}) {
102 Future onExit = singleResponseFuture(isolate.addOnExitListener);
103 if (Duration.ZERO == timeout) {
104 isolate.kill(priority: Isolate.IMMEDIATE);
105 return onExit;
106 } else {
107 // Try a more gentle shutdown sequence.
108 _commandPort.send(list1(_SHUTDOWN));
109 return onExit.timeout(timeout, onTimeout: () {
110 isolate.kill(priority: Isolate.IMMEDIATE);
111 return onExit;
112 });
113 }
114 }
115
116 /// Queries the isolate on whether it's alive.
117 ///
118 /// If the isolate is alive and responding to commands, the
119 /// returned future completes with `true`.
120 ///
121 /// If the other isolate is not alive (like after calling [kill]),
122 /// or doesn't answer within [timeout] for any other reason,
123 /// the returned future completes with `false`.
124 ///
125 /// Guaranteed to only complete after all previous sent isolate commands
126 /// (like pause and resume) have been handled.
127 /// Paused isolates do respond to ping requests.
128 Future<bool> ping({Duration timeout: const Duration(seconds: 1)}) {
129 var channel = new SingleResponseChannel(callback: _kTrue,
130 timeout: timeout,
131 timeoutValue: false);
132 isolate.ping(channel.port);
133 return channel.result;
134 }
135
136 static bool _kTrue(_) => true;
137
138 /// Pauses the isolate.
139 ///
140 /// While paused, no normal messages are processed, and calls to [run] will
141 /// be delayed until the isolate is resumed.
142 ///
143 /// Commands like [kill] and [ping] are still executed while the isolate is
144 /// paused.
145 ///
146 /// If [resumeCapability] is omitted, it defaults to the [isolate]'s
147 /// [Isolate.pauseCapability].
148 ///
149 /// Calling pause more than once with the same `resumeCapability`
150 /// has no further effect. Only a single call to [resume] is needed
151 /// to resume the isolate.
152 void pause([Capability resumeCapability]) {
153 if (resumeCapability == null) resumeCapability = isolate.pauseCapability;
154 isolate.pause(resumeCapability);
155 }
156
157 /// Resumes after a [pause].
158 ///
159 /// If [resumeCapability] is omitted, it defaults to the isolate's
160 /// [Isolate.pauseCapability].
161 ///
162 /// Even if `pause` has been called more than once with the same
163 /// `resumeCapability`, a single resume call with stop the pause.
164 void resume([Capability resumeCapability]) {
165 if (resumeCapability == null) resumeCapability = isolate.pauseCapability;
166 isolate.resume(resumeCapability);
167 }
168
169 /// Execute `function(argument)` in the isolate and return the result.
170 ///
171 /// Sends [function] and [argument] to the isolate, runs the call, and
172 /// returns the result, whether it returned a value or threw.
173 /// If the call returns a [Future], the final result of that future
174 /// will be returned.
175 ///
176 /// This works similar to the arguments to [Isolate.spawn], except that
177 /// it runs in the existing isolate and the return value is returned to
178 /// the caller.
179 ///
180 /// Example:
181 ///
182 /// IsolateRunner iso = await IsolateRunner.spawn();
183 /// try {
184 /// return await iso.run(heavyComputation, argument);
185 /// } finally {
186 /// await iso.close();
187 /// }
188 Future run(function(argument), argument, {Duration timeout, onTimeout()}) {
189 return singleResultFuture((SendPort port) {
190 _commandPort.send(list4(_RUN, function, argument, port));
191 }, timeout: timeout, onTimeout: onTimeout);
192 }
193
194 /// A broadcast stream of uncaught errors from the isolate.
195 ///
196 /// When listening on the stream, errors from the isolate will be reported
197 /// as errors in the stream. Be ready to handle the errors.
198 ///
199 /// The stream closes when the isolate shuts down.
200 Stream get errors {
201 StreamController controller;
202 RawReceivePort port;
203 void handleError(message) {
204 if (message == null) {
205 // Isolate shutdown.
206 port.close();
207 controller.close();
208 } else {
209 // Uncaught error.
210 String errorDescription = message[0];
211 String stackDescription = message[1];
212 var error = new RemoteError(errorDescription, stackDescription);
213 controller.addError(error, error.stackTrace);
214 }
215 }
216 controller = new StreamController.broadcast(
217 sync: true,
218 onListen: () {
219 port = new RawReceivePort(handleError);
220 isolate.addErrorListener(port.sendPort);
221 isolate.addOnExitListener(port.sendPort);
222 },
223 onCancel: () {
224 isolate.removeErrorListener(port.sendPort);
225 isolate.removeOnExitListener(port.sendPort);
226 port.close();
227 port = null;
228 });
229 return controller.stream;
230 }
231
232 /// Waits for the [isolate] to terminate.
233 ///
234 /// Completes the returned future when the isolate terminates.
235 ///
236 /// If the isolate has already stopped responding to commands,
237 /// the returned future will be completed after one second,
238 /// using [ping] to check if the isolate is still alive.
239 Future get onExit {
240 // TODO(lrn): Is there a way to see if an isolate is dead
241 // so we can close the receive port for this future?
242 if (_onExitFuture == null) {
243 var channel = new SingleResponseChannel();
244 isolate.addOnExitListener(channel.port);
245 _onExitFuture = channel.result;
246 ping().then((bool alive) {
247 if (!alive) {
248 channel.interrupt();
249 _onExitFuture = null;
250 }
251 });
252 }
253 return _onExitFuture;
254 }
255 }
256
257 /// The remote part of an [IsolateRunner].
258 ///
259 /// The `IsolateRunner` sends commands to the controlled isolate through
260 /// the `IsolateRunnerRemote` [commandPort].
261 ///
262 /// Only use this class if you need to set up the isolate manually
263 /// instead of relying on [IsolateRunner.spawn].
264 class IsolateRunnerRemote {
265 final RawReceivePort _commandPort = new RawReceivePort();
266 IsolateRunnerRemote() {
267 _commandPort.handler = _handleCommand;
268 }
269
270 /// The command port that can be used to send commands to this remote.
271 ///
272 /// Use this as argument to [new IsolateRunner] if creating the link
273 /// manually, otherwise it's handled by [IsolateRunner.spawn].
274 SendPort get commandPort => _commandPort.sendPort;
275
276 static void _create(SendPort initPort) {
277 var remote = new IsolateRunnerRemote();
278 initPort.send(remote.commandPort);
279 }
280
281 void _handleCommand(List command) {
282 switch (command[0]) {
283 case _SHUTDOWN:
284 SendPort responsePort = command[1];
285 _commandPort.close();
286 responsePort.send(null);
287 return;
288 case _RUN:
289 Function function = command[1];
290 var argument = command[2];
291 SendPort responsePort = command[3];
292 sendFutureResult(new Future.sync(() => function(argument)),
293 responsePort);
294 return;
295 }
296 }
297 }
OLDNEW
« no previous file with comments | « packages/isolate/lib/isolate.dart ('k') | packages/isolate/lib/load_balancer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698