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

Side by Side Diff: mojo/public/dart/src/handle_watcher.dart

Issue 1132063007: Rationalize Dart mojo and sky package structure (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 7 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 | « mojo/public/dart/src/handle.dart ('k') | mojo/public/dart/src/message.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 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 part of internal;
6
7 // The MojoHandleWatcher sends a stream of events to application isolates that
8 // register Mojo handles with it. Application isolates make the following calls:
9 //
10 // add(handle, port, signals) - Instructs the MojoHandleWatcher isolate to add
11 // 'handle' to the set of handles it watches, and to notify the calling
12 // isolate only for the events specified by 'signals' using the send port
13 // 'port'
14 //
15 // remove(handle) - Instructs the MojoHandleWatcher isolate to remove 'handle'
16 // from the set of handles it watches. This allows the application isolate
17 // to, e.g., pause the stream of events.
18 //
19 // close(handle) - Notifies the HandleWatcherIsolate that a handle it is
20 // watching should be removed from its set and closed.
21 class MojoHandleWatcher {
22 // Control commands.
23 static const int ADD = 0;
24 static const int REMOVE = 1;
25 static const int CLOSE = 2;
26 static const int TIMER = 3;
27 static const int SHUTDOWN = 4;
28
29 static const int kMojoHandleInvalid = 0;
30 static const int kDeadlineIndefinite = -1;
31
32 static const int kMojoResultOk = 0;
33 static const int kMojoResultDeadlineExceeded = 4;
34 static const int kMojoResultFailedPrecondition = 9;
35
36 static const int kMojoSignalsReadable = (1 << 0);
37 static const int kMojoSignalsWritable = (1 << 1);
38 static const int kMojoSignalsPeerClosed = (1 << 2);
39 static const int kMojoSignalsAll =
40 kMojoSignalsReadable | kMojoSignalsWritable | kMojoSignalsPeerClosed;
41
42 static int _encodeCommand(int cmd, [int signals = 0]) =>
43 (cmd << 3) | (signals & kMojoSignalsAll);
44 static int _decodeCommand(int cmd) {
45 assert(kMojoSignalsAll < 1 << 3);
46 return cmd >> 3;
47 }
48 static int _decodeSignals(int cmd) {
49 return cmd & kMojoSignalsAll;
50 }
51
52 // The Mojo handle over which control messages are sent.
53 int _controlHandle;
54
55 // Whether the handle watcher should shut down.
56 bool _shutdown;
57
58 // The list of handles being watched.
59 List<int> _handles;
60 int _handleCount;
61
62 // A port for each handle on which to send events back to the isolate that
63 // owns the handle.
64 List<SendPort> _ports;
65
66 // The signals that we care about for each handle.
67 List<int> _signals;
68
69 // A mapping from Mojo handles to their indices in _handles.
70 Map<int, int> _handleIndices;
71
72 // Priority queue of timers registered with the watcher.
73 TimerQueue _timerQueue;
74
75 MojoHandleWatcher(this._controlHandle)
76 : _shutdown = false,
77 _handles = new List<int>(),
78 _ports = new List<SendPort>(),
79 _signals = new List<int>(),
80 _handleIndices = new Map<int, int>(),
81 _handleCount = 1,
82 _timerQueue = new TimerQueue() {
83 // Setup control handle.
84 _handles.add(_controlHandle);
85 _ports.add(null); // There is no port for the control handle.
86 _signals.add(kMojoSignalsReadable);
87 _handleIndices[_controlHandle] = 0;
88 }
89
90 static void _handleWatcherIsolate(int consumerHandle) {
91 MojoHandleWatcher watcher = new MojoHandleWatcher(consumerHandle);
92 while (!watcher._shutdown) {
93 int deadline = watcher._processTimerDeadlines();
94 // mwmr[0]: result, mwmr[1]: index, mwmr[2]: list of signal states.
95 List mwmr = MojoHandleNatives.waitMany(
96 watcher._handles, watcher._signals, deadline);
97 if ((mwmr[0] == kMojoResultOk) && (mwmr[1] == 0)) {
98 watcher._handleControlMessage();
99 } else if ((mwmr[0] == kMojoResultOk) && (mwmr[1] > 0)) {
100 int handle = watcher._handles[mwmr[1]];
101
102 // Route event.
103 watcher._routeEvent(mwmr[2][mwmr[1]][0], mwmr[1]);
104 // Remove the handle from the list.
105 watcher._removeHandle(handle);
106 } else if (mwmr[0] != kMojoResultDeadlineExceeded) {
107 // Some handle was closed, but not by us.
108 // Find it and close it on our side.
109 watcher._pruneClosedHandles(mwmr[2]);
110 }
111 }
112 }
113
114 void _routeEvent(int satisfiedSignals, int idx) {
115 _ports[idx].send([_signals[idx], satisfiedSignals & _signals[idx]]);
116 }
117
118 void _handleControlMessage() {
119 List result = MojoHandleWatcherNatives.recvControlData(_controlHandle);
120 // result[0] = mojo handle if any, or a timer deadline in milliseconds.
121 // result[1] = SendPort if any.
122 // result[2] = command << 2 | WRITABLE | READABLE
123
124 var signals = _decodeSignals(result[2]);
125 int command = _decodeCommand(result[2]);
126 switch (command) {
127 case ADD:
128 _addHandle(result[0], result[1], signals);
129 break;
130 case REMOVE:
131 _removeHandle(result[0]);
132 break;
133 case CLOSE:
134 _close(result[0], result[1]);
135 break;
136 case TIMER:
137 _timer(result[1], result[0]);
138 break;
139 case SHUTDOWN:
140 _shutdownHandleWatcher(result[1]);
141 break;
142 default:
143 throw "Invalid Command: $command";
144 break;
145 }
146 }
147
148 void _addHandle(int mojoHandle, SendPort port, int signals) {
149 int idx = _handleIndices[mojoHandle];
150 if (idx == null) {
151 _handles.add(mojoHandle);
152 _ports.add(port);
153 _signals.add(signals);
154 _handleIndices[mojoHandle] = _handleCount;
155 _handleCount++;
156 } else {
157 assert(_ports[idx] == port);
158 assert(_handles[idx] == mojoHandle);
159 _signals[idx] |= signals;
160 }
161 }
162
163 void _removeHandle(int mojoHandle) {
164 int idx = _handleIndices[mojoHandle];
165 if (idx == null) {
166 throw "Remove on a non-existent handle: idx = $idx.";
167 }
168 if (idx == 0) {
169 throw "The control handle (idx = 0) cannot be removed.";
170 }
171 // We don't use List.removeAt so that we know how to fix-up _handleIndices.
172 if (idx == _handleCount - 1) {
173 int handle = _handles[idx];
174 _handleIndices[handle] = null;
175 _handles.removeLast();
176 _signals.removeLast();
177 _ports.removeLast();
178 _handleCount--;
179 } else {
180 int last = _handleCount - 1;
181 _handleIndices[_handles[idx]] = null;
182 _handles[idx] = _handles[last];
183 _signals[idx] = _signals[last];
184 _ports[idx] = _ports[last];
185 _handles.removeLast();
186 _signals.removeLast();
187 _ports.removeLast();
188 _handleIndices[_handles[idx]] = idx;
189 _handleCount--;
190 }
191 }
192
193 void _close(int mojoHandle, SendPort port, {bool pruning: false}) {
194 assert(!pruning || (port == null));
195 int idx = _handleIndices[mojoHandle];
196 if (idx == null) {
197 // An app isolate may request that the handle watcher close a handle that
198 // has already been pruned. This happens when the app isolate has not yet
199 // received the PEER_CLOSED event. The app isolate will not close the
200 // handle, so we must do so here.
201 MojoHandleNatives.close(mojoHandle);
202 if (port != null) port.send(null); // Notify that close is done.
203 return;
204 }
205 if (idx == 0) {
206 throw "The control handle (idx = 0) cannot be closed.";
207 }
208 MojoHandleNatives.close(_handles[idx]);
209 if (port != null) port.send(null); // Notify that close is done.
210 if (pruning) {
211 // If this handle is being pruned, notify the application isolate
212 // by sending MojoHandleSignals.PEER_CLOSED.
213 _ports[idx].send([_signals[idx], kMojoSignalsPeerClosed]);
214 }
215 _removeHandle(mojoHandle);
216 }
217
218 // Returns the next timer deadline in units of microseconds from 'now'.
219 int _processTimerDeadlines() {
220 int now = (new DateTime.now()).millisecondsSinceEpoch;
221 while (_timerQueue.hasTimer && (now >= _timerQueue.currentTimeout)) {
222 _timerQueue.currentPort.send(null);
223 _timerQueue.removeCurrent();
224 now = (new DateTime.now()).millisecondsSinceEpoch;
225 }
226 return _timerQueue.hasTimer
227 ? (_timerQueue.currentTimeout - now) * 1000
228 : kDeadlineIndefinite;
229 }
230
231 void _timer(SendPort port, int deadline) {
232 _timerQueue.updateTimer(port, deadline);
233 }
234
235 void _pruneClosedHandles(List<List<int>> states) {
236 List<int> closed = new List();
237 for (var i = 0; i < _handles.length; i++) {
238 if (states != null) {
239 int signals = states[i][0];
240 if ((signals & kMojoSignalsPeerClosed) != 0) {
241 closed.add(_handles[i]);
242 }
243 } else {
244 List mwr = MojoHandleNatives.wait(_handles[i], kMojoSignalsAll, 0);
245 if ((mwr[0] != kMojoResultOk) &&
246 (mwr[0] != kMojoResultDeadlineExceeded)) {
247 closed.add(_handles[i]);
248 }
249 }
250 }
251 for (var h in closed) {
252 _close(h, null, pruning: true);
253 }
254 // '_close' updated the '_handles' array, so at this point the '_handles'
255 // array and the caller's 'states' array are mismatched.
256 }
257
258 void _shutdownHandleWatcher(SendPort shutdownSendPort) {
259 _shutdown = true;
260 MojoHandleNatives.close(_controlHandle);
261 shutdownSendPort.send(null);
262 }
263
264 static int _sendControlData(int rawHandle, SendPort port, int data) {
265 int controlHandle = MojoHandleWatcherNatives.getControlHandle();
266 if (controlHandle == kMojoHandleInvalid) {
267 return kMojoResultFailedPrecondition;
268 }
269
270 var result = MojoHandleWatcherNatives.sendControlData(
271 controlHandle, rawHandle, port, data);
272 return result;
273 }
274
275 // Starts up the MojoHandleWatcher isolate. Should be called only once
276 // per VM process.
277 static Future<Isolate> _start() {
278 // Make a control message pipe,
279 List pipeEndpoints = MojoMessagePipeNatives.MojoCreateMessagePipe(0);
280 assert(pipeEndpoints != null);
281 assert((pipeEndpoints is List) && (pipeEndpoints.length == 3));
282 assert(pipeEndpoints[0] == kMojoResultOk);
283
284 int consumerHandle = pipeEndpoints[1];
285 int producerHandle = pipeEndpoints[2];
286
287 // Call setControlHandle with the other end.
288 assert(producerHandle != kMojoHandleInvalid);
289 MojoHandleWatcherNatives.setControlHandle(producerHandle);
290
291 // Spawn the handle watcher isolate with the MojoHandleWatcher,
292 return Isolate.spawn(_handleWatcherIsolate, consumerHandle);
293 }
294
295 // Causes the MojoHandleWatcher isolate to exit. Should be called only
296 // once per VM process.
297 static void _stop() {
298 // Create a port for notification that the handle watcher has shutdown.
299 var shutdownReceivePort = new ReceivePort();
300 var shutdownSendPort = shutdownReceivePort.sendPort;
301
302 // Send the shutdown command.
303 _sendControlData(
304 kMojoHandleInvalid, shutdownSendPort, _encodeCommand(SHUTDOWN));
305
306 // Close the control handle.
307 int controlHandle = MojoHandleWatcherNatives.getControlHandle();
308 MojoHandleNatives.close(controlHandle);
309
310 // Invalidate the control handle.
311 MojoHandleWatcherNatives.setControlHandle(kMojoHandleInvalid);
312
313 // Wait for the handle watcher isolate to exit.
314 shutdownReceivePort.first.then((_) {
315 shutdownReceivePort.close();
316 });
317 }
318
319 // If wait is true, returns a future that resolves only after the handle
320 // has actually been closed by the handle watcher. Otherwise, returns a
321 // future that resolves immediately.
322 static Future<int> close(int mojoHandle, {bool wait: false}) {
323 //assert(MojoHandle._removeUnclosedHandle(mojoHandle));
324 if (!wait) {
325 return new Future.value(
326 _sendControlData(mojoHandle, null, _encodeCommand(CLOSE)));
327 }
328 int result;
329 var completer = new Completer();
330 var rawPort = new RawReceivePort((_) {
331 completer.complete(result);
332 });
333 result =
334 _sendControlData(mojoHandle, rawPort.sendPort, _encodeCommand(CLOSE));
335 return completer.future.then((r) {
336 rawPort.close();
337 return r;
338 });
339 }
340
341 static int add(int mojoHandle, SendPort port, int signals) {
342 return _sendControlData(mojoHandle, port, _encodeCommand(ADD, signals));
343 }
344
345 static int remove(int mojoHandle) {
346 return _sendControlData(mojoHandle, null, _encodeCommand(REMOVE));
347 }
348
349 static int timer(Object ignored, SendPort port, int deadline) {
350 // The deadline will be unwrapped before sending to the handle watcher.
351 return _sendControlData(deadline, port, _encodeCommand(TIMER));
352 }
353 }
OLDNEW
« no previous file with comments | « mojo/public/dart/src/handle.dart ('k') | mojo/public/dart/src/message.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698